**C# の文字列が Dapper で SQL Server インデックスを静かに破壊する理由**

Dapper を使って SQL Server データベースへクエリを投げる際、文字列結合や文字列補間(string interpolation)でクエリを作成することはよくあります。  
しかし、この一見無害な手法がインデックスの性能を黙って破壊してしまうケースがあります。

---

## なぜ起きるのか

1. **暗黙の型変換**  
   `string` と `int`・`bool` など非文字列型を結合すると、SQL Server は列値を `nvarchar` に変換せざるを得ません。  
2. **インデックス回避**  
   この暗黙変換により最適化器は既存の数値や日付インデックスを利用できず、フルテーブルスキャンが発生します。

---

## 問題を引き起こす典型的なパターン

| パターン | 何をしているか | インデックスへの影響 |
|---------|-----------------|----------------------|
| `WHERE Id = " + id`(文字列結合) | `Id` 列を `nvarchar` に変換 | フルスキャン |
| `$"SELECT * FROM Users WHERE IsActive = {isActive}"`(補間) | ブール値も同様に `nvarchar` へ変換 | フルスキャン |
| `WHERE CreatedDate >= @date.ToString()` | 日付を文字列へ変換 | インデックスが失われる |

---

## 修正方法

1. **インライン値ではなくパラメータを使用する**  
   ```csharp
   var sql = "SELECT * FROM Users WHERE Id = @Id";
   connection.Query<User>(sql, new { Id = id });
   ```
2. **型の一貫性を保つ**  
   列が期待する正確な型(`int`、`DateTime` など)で渡す。  
3. **C# で暗黙変換を避ける**  
   必要なら明示的にキャストまたは変換し、安全かつ意図した変換のみ行う。

---

## 簡易チェックリスト

- [ ] Dapper に渡す値は文字列化せず、型付きである。  
- [ ] 変数データと SQL フラグメントのインライン結合を行わない。  
- [ ] すべてのクエリに `@ParameterName` プレースホルダーを使用する。

これらのガイドラインに従えば、インデックスの整合性を保ちつつクエリを高速かつ効率的に維持できます。

2026/03/07 7:55

**C# の文字列が Dapper で SQL Server インデックスを静かに破壊する理由** Dapper を使って SQL Server データベースへクエリを投げる際、文字列結合や文字列補間(string interpolation)でクエリを作成することはよくあります。 しかし、この一見無害な手法がインデックスの性能を黙って破壊してしまうケースがあります。 --- ## なぜ起きるのか 1. **暗黙の型変換** `string` と `int`・`bool` など非文字列型を結合すると、SQL Server は列値を `nvarchar` に変換せざるを得ません。 2. **インデックス回避** この暗黙変換により最適化器は既存の数値や日付インデックスを利用できず、フルテーブルスキャンが発生します。 --- ## 問題を引き起こす典型的なパターン | パターン | 何をしているか | インデックスへの影響 | |---------|-----------------|----------------------| | `WHERE Id = " + id`(文字列結合) | `Id` 列を `nvarchar` に変換 | フルスキャン | | `$"SELECT * FROM Users WHERE IsActive = {isActive}"`(補間) | ブール値も同様に `nvarchar` へ変換 | フルスキャン | | `WHERE CreatedDate >= @date.ToString()` | 日付を文字列へ変換 | インデックスが失われる | --- ## 修正方法 1. **インライン値ではなくパラメータを使用する** ```csharp var sql = "SELECT * FROM Users WHERE Id = @Id"; connection.Query<User>(sql, new { Id = id }); ``` 2. **型の一貫性を保つ** 列が期待する正確な型(`int`、`DateTime` など)で渡す。 3. **C# で暗黙変換を避ける** 必要なら明示的にキャストまたは変換し、安全かつ意図した変換のみ行う。 --- ## 簡易チェックリスト - [ ] Dapper に渡す値は文字列化せず、型付きである。 - [ ] 変数データと SQL フラグメントのインライン結合を行わない。 - [ ] すべてのクエリに `@ParameterName` プレースホルダーを使用する。 これらのガイドラインに従えば、インデックスの整合性を保ちつつクエリを高速かつ効率的に維持できます。

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

概要:
.NET/Dapper アプリケーションでは、C# の文字列を

nvarchar(4000)
として渡すと、SQL Server が
varchar
列に対して暗黙の型変換(implicit conversions)を実行します。これにより、インデックス検索がスキャンに置き換わり、論理読み取り数が単桁から数万に膨らみ、CPU/I/O の使用率が急増します(例:
CONVERT_IMPLICIT(nvarchar(255), [Sales].[ProductCode], 0)
)。
正確性には影響しませんが、実行計画や Query Store の警告で明らかになります。特に
SQL_Latin1_General_CP1_CI_AS
などの照合順序では顕著です。

修正: パラメータを ANSI として明示的に宣言し、列サイズと一致させます。

var p = new DynamicParameters();
p.Add("productCode", productCode, DbType.AnsiString, size: 100);
await conn.QueryFirstOrDefaultAsync<Product>(sql, p);

または匿名オブジェクトを使用する場合:

new { productCode = new DbString { Value = productCode, IsAnsi = true, Length = 100 } }

スキーマ変更、インデックス更新、クエリ書き換えは不要です。パフォーマンスの改善は即座に実感できます。

監査ヒント: Query Store で

@nvarchar(4000)
を検索し、varchar 列へ文字列を渡す匿名オブジェクトをコード内でスキャンしてください。

ベストプラクティス: 将来のリグレッション防止のために、

DbType.AnsiString
(または
IsAnsi = true
)を使用した理由をコメントしておくことが推奨されます。

この簡単な調整でサーバー負荷を低減し、コスト削減とスケールアップが実現します。結果として開発者・ユーザー・広範な .NET/SQL Server コミュニティ全体に恩恵をもたらします。

本文

最近直面した本番環境のパフォーマンス問題

アプリケーションが「熱い」状態で動作していました。CPU は平均 50 % を超え、ピーク時には 90 % に達します。診断スナップショットを取得し、CPU 時間の高いクエリから順に解析を開始しました。

一番問題になっていたのは?
インデックス付きカラムに対して単純な WHERE を使った Dapper クエリです。高速であるべきなのに、1 回実行あたり数千ミリ秒という CPU 時間が発生し、日々数十万回の実行で「2 文字型の不一致」が C# コード上では全く見えませんでした。


実際に何が起きているのか?

ほぼすべての .NET プロジェクトで Dapper を使うと、次のようなパターンが現れます。

const string sql = "SELECT * FROM Products WHERE ProductCode = @productCode";
var result = await connection.QueryFirstOrDefaultAsync<Product>(sql, new { productCode });

シンプルでクリーン。

ProductCode
がデータベース上の varchar カラムなら、静かにパフォーマンスを破壊します。

C# の文字列を匿名オブジェクト経由で渡すと、Dapper は

nvarchar(4000)
にマッピングします。これは ADO.NET で
System.String
がデフォルトで使用する型です。安全なデフォルトとしては妥当ですが、カラムが varchar の場合、SQL Server は比較前に列のすべての値を
nvarchar
に変換しなくてはいけません。この変換は CONVERT_IMPLICIT と呼ばれ、インデックスを使えないため毎回フルスキャンになります。

実行計画で次のように現れます:

CONVERT_IMPLICIT(nvarchar(255), [Sales].[ProductCode], 0)

SQL Server が「完璧なインデックスがあったはずなのに、Unicode パラメータと比較するためにすべての行を変換せざるを得ない」と告げています。


実際にどれほど悪影響があるか?

計算で見ると凄まじいです。1 億行程度のテーブルで

ProductCode
に非クラスタ化インデックスがあると仮定します。正しいパラメータ型なら SQL Server は インデックスシーク を実行し、数十ミリオロジカルリード(マイクロ秒単位)で済みます。

一方、暗黙変換が発生すると インデックススキャン が走り、インデックス内のすべての行を読み取り、各値を変換して比較します。数十万の論理リードに拡大し、クエリ実行回数を掛け合わせれば CPU の重大な問題となります。

当社では 1 クエリがデータベースサーバ全体の CPU 消費量のかなりの部分を占めていました。複雑さやインデックス不良ではなく、パラメータ型のミスマッチだけで発生していたのです。

注:照合順序
影響はデータベースの照合順序によって異なります。最も一般的な

SQL_Latin1_General_CP1_CI_AS
ではフルインデックススキャンが発生し、最悪ケースです。一部 Windows 照合順序(例:
Latin1_General_CI_AS
)でもインデックスシークが可能ですが、暗黙変換のオーバーヘッドは残ります。いずれにせよ、パラメータ型を列型と一致させることが正解です。


修正方法

修正はほぼ極めて簡単です:Dapper に varchar であることを明示します。

DynamicParameters
DbType.AnsiString
を使います。

const string sql = "SELECT * FROM Products WHERE ProductCode = @productCode";

var parameters = new DynamicParameters();
parameters.Add("productCode", productCode, DbType.AnsiString, size: 100);

var result = await connection.QueryFirstOrDefaultAsync<Product>(sql, parameters);

DbType.AnsiString
は ADO.NET に
varchar
パラメータを送るよう指示します。デフォルトの
DbType.String
(C# の文字列)では
nvarchar
が送られます。

size
はカラム定義に合わせてください。例えば
varchar(255)
なら
size: 255
とします。これで SQL Server はパラメータ型を列型と正確に一致させ、クエリプランの再利用も効率的になります。

匿名オブジェクトを好む場合は、より簡潔な

DbString
を使用できます:

var result = await connection.QueryFirstOrDefaultAsync<Product>(sql,
    new { productCode = new DbString { Value = productCode, IsAnsi = true, Length = 100 } });

どちらの方法も同じ結果、すなわち

varchar
パラメータを送ります。


前後比較

指標修正前(nvarchar)修正後(varchar)
スキャンタイプINDEX SCANINDEX SEEK
論理リード数数万数十
実行あたり CPUミリ秒マイクロ秒

スキーマ変更・インデックス追加・クエリ書き換えは不要。Dapper に正しいパラメータ型を渡すだけです。


アプリケーションでの検出方法

この問題があるか疑う場合、次の手順で確認できます。

  1. Query Store で暗黙変換を検索

    SELECT TOP 20
        qsqt.query_sql_text,
        qsrs.avg_cpu_time,
        qsrs.count_executions
    FROM sys.query_store_runtime_stats qsrs
    JOIN sys.query_store_plan qsp ON qsrs.plan_id = qsp.plan_id
    JOIN sys.query_store_query qsq ON qsp.query_id = qsq.query_id
    JOIN sys.query_store_query_text qsqt ON qsq.query_text_id = qsqt.query_text_id
    WHERE qsqt.query_sql_text LIKE '%@%nvarchar(4000)%'
    ORDER BY qsrs.avg_cpu_time * qsrs.count_executions DESC;
    
  2. 実行計画で

    CONVERT_IMPLICIT
    を探す
    varchar
    カラムに対してフィルタリングされるクエリに現れたら検出です。

  3. C# コードを検索
    匿名オブジェクト経由で文字列パラメータを渡し、対象が

    varchar
    カラムの場合:

    // これが問題のパターン
    await connection.QueryAsync<T>(sql, new { someVarcharColumn });
    

原則

  • 列が varchar の場合は
    DbType.AnsiString
    を使用。
  • 列が nvarchar の場合はデフォルトの
    DbType.String
    で問題なし。
  • パラメータ型を列型と合わせ、サイズも一致させる。

ヒント

DynamicParameters
を使う理由をコメントで説明しておくと、将来リファクタリング時に「簡素化」されて再び
new { productCode }
になり、問題が戻ってきません。

var parameters = new DynamicParameters();
// DbType.AnsiString 必須: Products.ProductCode は varchar(100)。
 // これを指定しないと Dapper は nvarchar(4000) を送信し、毎行 CONVERT_IMPLICIT が発生
 // インデックスシークが失われます。
parameters.Add("productCode", productCode, DbType.AnsiString, size: 100);

クエリの監査を実施

このバグはほぼ見えないものです。コードは正しく、クエリは正しい結果を返し、ログにもエラーが出ません。ただ遅く動くだけで、原因が分からない状態に陥ります。実行計画や Query Store データを調べるまで問題の所在がわかりません。

Dapper と SQL Server を使用していて、列が

varchar
の場合は今日中にパラメータ使用方法を監査しましょう。匿名オブジェクトで文字列を渡すすべてのケースが、表面上は正しく見えるフルテーブルスキャンを隠しています。


さらに読む

同じ日のほかのニュース

一覧に戻る →

2026/03/07 6:52

「このCSSは、私が人間であることを証明します。」

## Japanese Translation: (以下に翻訳文を記載します) **著者は、選択的な大文字化、CSS を用いた対象的なケース変換(`text-transform: lowercase`)、慎重に使われる em ダッシュなどの微妙なタイポグラフィック・選択が、ファイルを `tr` でパイプするような鈍い自動化手法よりも優れていると主張しています。大文字化は「最初の傷」として描かれますが、実際には予想ほど痛みを伴わず、必要に応じて単語が大文字で流れ出します。著者は粗末な `cat post.md | tr A‑Z a‑z | sponge post.md` の手法を却下し、よりクリーンな効果を持つ CSS を推奨しています。 em ダッシュは貴重とされますが、作家の真実の自分を露呈させないように隠したままである必要があります。モノスペースフォントはテキストの美学を損なうため拒否されています。小さなスクリプト(`uv run rewrite_font.py`)は文字形態を微調整するための取るべき手段として強調され、意図的に単語を誤字すること(例: “their/there”、 “its/it’s”)がスタイルの一部であると述べられていますが、“Definately?” のような問題のあるペアは避けるとしています。 作家はノーリグ・コーパスを参照し、単語選択を導くとともに、ターゲットとなる単語から「u」を迅速に除去することで手法の精密さを示しています。全体的なトーンは、書くことが外見だけでなく思考・推論・関与を反映するものであると強調しています。 以前の拒否(“No. Not today.”)はスタイリスティックオーバーホールへの抵抗を示しています。次に計画されている変更は、作家自身の自我感覚を変える唯一の真に重要なステップとして描かれています。 技術的読者――特に文書スタイリング、フォント操作、編集ワークフローに関わる人々――に対して、このメッセージは自動変換から離れ、意図的なタイポグラフィック決定へ移行することを奨励し、デザイン標準や編集実務の再構築につながり得ると述べています。

2026/03/07 6:19

**IPリースの陰影ある世界**

## Japanese Translation: IPリースは、誰もがクリーンで匿名のIPv4アドレスを取得し、ジオロケーションデータを操作できる隠密レンタル市場を生み出しており、IPベースの評判システムの信頼性を損なっています。ブロックを保有し、標準的な地域インターネットレジストリ(RIR)手順外でサブリースすることで、これらのサービスはWHOISトレーサビリティとRIRアカウンタビリティチェーンを迂回します。リース会社は料金を払ってブラックリスト化された範囲を「クリーン」し、ドロップダウンまたはCSVアップロードで任意の住宅または商業ジオロケーションを割り当て、WHOIS国フィールドまで操作することができ、MaxMind、Cloudflare、Googleなどに偽情報を供給します。 主要なVPNおよびプロキシプロバイダー(例:NordVPN、ExpressVPN、CyberGhost、PIA)は、LogicWeb、IPXO、INIZ、IPFoxi、Heficed、AnyIP/IPv4Deals などのリース会社からIPを調達しています。これらのプラットフォームは多くの場合、同じサイトで生IPスペースと完全なプロキシサブスクリプションの両方を販売し、エンドツーエンドの匿名化パイプラインを構築します。一部のプロバイダーは住宅ISPと直接提携してトラフィックを実際の加入者ネットワーク経由でルーティングし、合法的なオペレーターとプロキシとの境界を曖昧にしています。 業界は法的グレイズゾーンで運営されており—IPリースやジオロケーション操作を明示的に禁じる法律がないため—実効性の低い執行とインセンティブの不整合が生じています。リースが拡大するにつれて、インターネットセキュリティの基盤であるIP評判リスト、WHOIS帰属、およびジオロケーションデータベースはさらに侵食され、大規模な位置・所有権・評判の偽装が可能になります。これはユーザー(ボット検出やレート制限での誤検知)とIPベースのセキュリティ決定に依存する企業を脅かし、最終的にはインターネット全体の説明責任と安全性を弱めます。

2026/03/06 20:53

**AnthropicのレッドチームによるFirefoxの強化** 1. **背景と目的** * レッドチームは実際に起こり得る攻撃を模倣するセキュリティ研究グループです。 * 彼らの目標は、悪用される前にFirefox内の脆弱性を発見することです。 2. **手法** * **ターゲットスキャン** – 自動化ツールで既知の弱点をブラウザ上で検出します。 * **マニュアルエクスプロイト** – セキュリティ研究者が独自にカスタム攻撃コードを作成し、深部を調査します。 * **レッドチーム演習** – 高度な敵対者が用いる戦術を模したシミュレーション攻撃です。 3. **主な発見点** * **メモリ破壊バグ** – use‑after‑freeやバッファオーバーフローなど複数の問題が検出されました。 * **権限昇格経路** – 悪意あるウェブページが高い権限を取得できる可能性があります。 * **プライバシー漏洩** – クロスオリジンリクエストにより予期せぬデータが流出するケースです。 4. **実施した対策** * メモリ安全チェックの更新(例:AddressSanitizer、Control Flow Integrity)。 * プロセス間通信を制限するサンドボックス境界の強化。 * プライバシーコントロールの改善と同一オリジンポリシーの厳格化。 5. **継続的取り組み** * レッドチームの調査結果をFirefoxリリースサイクルへ継続的に統合。 * Mozillaセキュリティチームと協力し、迅速なパッチ適用を実現。 * 責任ある開示ガイドラインに従い、重大脆弱性を公表。 6. **結論** * AnthropicのレッドチームはFirefoxのセキュリティ体制を強化する上で不可欠な役割を果たしています。 * 彼らの厳密なテストにより潜在的リスクが明らかになり、迅速な修正が行われることで最終ユーザーを高度脅威から保護できます。

## 日本語訳: 「Claude Opus 4.6 は、Mozilla の Firefox ブラウザにおける重大なセキュリティバグの発見と修正を非常に効果的に行い、同社が将来のセキュリティ研究で採用するようになったことを示しています。2 週間にわたる調査では、モデルは 22 の脆弱性を特定し、そのうち 14 は高重大度でした。ほとんどは Firefox 148.0 にて修正され、世界中の数億ユーザーに影響しました。最初の Use‑After‑Free バグは 20 分以内に発見され、Bugzilla に報告され、初期パッチ提案が提出されました。約 6,000 の C++ ファイル全体で Opus 4.6 は 112 件のユニークレポートを記録し、ほぼすべてが新しいリリースで解決されました。 エクスプロイトテスト(API クレジット約 4,000 ドル)は発見された脆弱性のうちわずか 2 件だけに成功しました。これは、テスト環境にサンドボックスなどの主要なブラウザ防御が欠如していたためです。結果は、AI が脆弱性検出を加速できることを示していますが、人間による検証と安全策が不可欠であることも明らかにしています。Mozilla の研究者たちは現在 Claude を社内で実験し、新しい言語モデルが検出とエクスプロイトのギャップを埋める方法を探っています。 Anthropic は「タスク・バリファイア」を推奨しており、AI エージェントの成果物が目標を達成したことを確認します。また、最小テストケース、概念実証(Proof‑of‑Concept)、候補パッチという三つの要素を強調し、メンテナが AI が生成したレポートを信頼できるようにしています。Claude Code Security は、脆弱性発見とパッチ作成機能を求める顧客やオープンソース保守者向けに限定的なリサーチプレビューとして利用可能です。将来のモデルが進化するにつれて、悪意ある乱用を防止しつつ、オープンソースエコシステム全体でより迅速なバグトリアージと高品質パッチを実現できるよう、より厳格な安全策が必要になります。」

**C# の文字列が Dapper で SQL Server インデックスを静かに破壊する理由** Dapper を使って SQL Server データベースへクエリを投げる際、文字列結合や文字列補間(string interpolation)でクエリを作成することはよくあります。 しかし、この一見無害な手法がインデックスの性能を黙って破壊してしまうケースがあります。 --- ## なぜ起きるのか 1. **暗黙の型変換** `string` と `int`・`bool` など非文字列型を結合すると、SQL Server は列値を `nvarchar` に変換せざるを得ません。 2. **インデックス回避** この暗黙変換により最適化器は既存の数値や日付インデックスを利用できず、フルテーブルスキャンが発生します。 --- ## 問題を引き起こす典型的なパターン | パターン | 何をしているか | インデックスへの影響 | |---------|-----------------|----------------------| | `WHERE Id = " + id`(文字列結合) | `Id` 列を `nvarchar` に変換 | フルスキャン | | `$"SELECT * FROM Users WHERE IsActive = {isActive}"`(補間) | ブール値も同様に `nvarchar` へ変換 | フルスキャン | | `WHERE CreatedDate >= @date.ToString()` | 日付を文字列へ変換 | インデックスが失われる | --- ## 修正方法 1. **インライン値ではなくパラメータを使用する** ```csharp var sql = "SELECT * FROM Users WHERE Id = @Id"; connection.Query<User>(sql, new { Id = id }); ``` 2. **型の一貫性を保つ** 列が期待する正確な型(`int`、`DateTime` など)で渡す。 3. **C# で暗黙変換を避ける** 必要なら明示的にキャストまたは変換し、安全かつ意図した変換のみ行う。 --- ## 簡易チェックリスト - [ ] Dapper に渡す値は文字列化せず、型付きである。 - [ ] 変数データと SQL フラグメントのインライン結合を行わない。 - [ ] すべてのクエリに `@ParameterName` プレースホルダーを使用する。 これらのガイドラインに従えば、インデックスの整合性を保ちつつクエリを高速かつ効率的に維持できます。 | そっか~ニュース