Missing N prefix on NVARCHAR literals silently mangles Japanese/Korean text and emoji without any error
公開日 2026-08-16 · 更新日 2026-08-16 · 最終検証日 2026-08-16
結論
When a string literal is assigned directly to an NVARCHAR column in SQL Server without the `N` prefix, the literal is implicitly converted to the connection's default code page before being stored as NVARCHAR. Characters that cannot be represented in that code page — Japanese, Korean, emoji, and the like — are lost, but this raises no error or warning; the value simply corrupts silently. ASCII-only test data never reveals the problem, so it is often discovered only after going to production, and the same pattern tends to recur.
この文書の適用条件
| 対象製品 | SQL Server (affects Unicode string literals in general) |
|---|---|
| 確認バージョン | SQL Server 2012 and later (including Azure SQL Database / Managed Instance) |
| 適用環境 | On-premises, EC2, Amazon RDS for SQL Server, Azure SQL |
| 必要権限 | Diagnostic SQL only needs SELECT on the target table. Source-code search needs read access to the repository |
| 実行影響 | All diagnostics in this article are read-only. Fixing/recovering already-corrupted data separately requires an UPDATE on the affected rows |
| 再起動 | Not required |
| 最終検証日 | 2026-08-16 |
そのまま実行できるコマンド
- 対象
- SQL Server 2012 and later / Azure SQL Database, Managed Instance
- 権限
- SELECT on the target table (metadata can be viewed with the public role)
- 変更作業
- None (read-only)
- Production実行
- Safe
-- 対象: SQL Server 2012 以降 / Azure SQL Database・Managed Instance
-- 権限: メタデータの参照はpublicロールで可。個々のデータ確認には対象テーブルのSELECT権限が必要
-- 変更作業: なし(参照のみ)
-- Production実行: 可能
-- 非ASCII文字を保持しうる nvarchar / nchar 列を一覧化する(調査対象の絞り込み用)
SELECT
t.name AS table_name,
c.name AS column_name,
ty.name AS type_name,
c.max_length
FROM sys.columns AS c
INNER JOIN sys.tables AS t
ON t.object_id = c.object_id
INNER JOIN sys.types AS ty
ON ty.user_type_id = c.user_type_id
WHERE ty.name IN ('nvarchar', 'nchar')
ORDER BY t.name, c.column_id;This list only narrows the scope of investigation — the mere existence of such a column is not itself a problem. The next step checks the actual data in each column.
- 対象
- The application / stored-procedure source-code repository
- 権限
- Read access to the repository
- 変更作業
- None (search only)
- Production実行
- Safe
# 対象: アプリケーション・ストアドプロシージャのソースコードリポジトリ
# 権限: リポジトリの読み取り権限
# 変更作業: なし(参照のみ)
# Production実行: 可能
#
# .sql ファイル内で、Nプレフィックスの無いシングルクォート文字列リテラルのうち
# 非ASCII文字を含むものを検出する(正規表現なので誤検知はあり、ヒット箇所は目視確認が前提)
grep -RnoP "(?<![Nn])'[^']*[^\x00-\x7F][^']*'" --include=*.sql .
# 文字列結合や sp_executesql で動的SQLを組み立てている箇所も洗い出す
grep -RniE "(sp_executesql|EXEC\(|exec\()" --include=*.sql --include=*.cs --include=*.ts .This also catches comments and intentional non-ASCII strings (such as error-message text), so every regex hit is a "candidate to investigate," not a "confirmed bug." Check each hit individually to see whether it is an assignment into an NVARCHAR column or parameter.
こういう状況で使います
- Inserting data containing Japanese, Korean, or emoji raises no error, but the stored value shows "?" or different characters
- Application tests (which use ASCII-only test data) show no problem; the issue surfaces only after going to production or loading real data
- It only happens on write paths that go through dynamic SQL (string concatenation or sp_executesql)
- The same symptom recurs in a different place or a different project, even in a codebase that was supposedly already "fixed"
考えられる原因(可能性の高い順)
01
The string literal is implicitly converted to the connection's default code page
A single-quoted string literal without the N prefix is first interpreted as a non-Unicode string, then converted to the connection's default code page before being assigned to the NVARCHAR type. Characters that cannot be represented in that code page are lost during the conversion.
02
Dynamic SQL is assembled by string concatenation without thinking about NVARCHAR
When building SQL statements via string concatenation or sp_executesql, it is easy to forget the N prefix on the literal being embedded — especially when the value is embedded directly as a string rather than passed as a parameter.
03
No error or warning is raised, so tests rarely catch it
Because neither a compile-time nor a runtime error occurs, ASCII-only test data looks perfectly fine. The symptom appears only once real non-ASCII data is written.
04
Once a character is lost, it cannot be recovered without an independent source
A character lost to implicit conversion no longer exists anywhere inside the NVARCHAR column. Unless there is some independent source — logs in another system, a copy of the original data — the correct value can never be restored afterward.
確認手順
- 1
Enumerate the nvarchar/nchar columns on the target table
参照のみUse the diagnostic SQL above to list columns that could hold non-ASCII characters.
- 2
Search the source code for missing N prefixes
参照のみUse grep to find suspicious string literals and places that build dynamic SQL, then check each one individually.
- 3
Check existing data for signs of corruption
参照のみFor columns that should hold non-ASCII text, individually check whether values have been replaced by unintended characters.
- 4
Check whether an independent source exists for the corrupted data
参照のみCheck whether some other source of truth — an external system's logs, a record from a different path — still holds the correct values.
対応方法
すぐに実施できる低リスクの対応
Add the N prefix to every NVARCHAR literal in new code
低Start with newly added or modified SQL code, and add the N prefix to existing non-ASCII string literals as well.
Fix the places that build dynamic SQL first
低Parts that assemble SQL via string concatenation or sp_executesql have the widest blast radius, so fix them first. Switch to parameterization where possible.
事前検討が必要な変更
Add it as a code-review checklist item
低Document, as an explicit review item, that string literals assigned to NVARCHAR columns must be checked.
Mechanically detect missing N prefixes in CI / pre-commit
中Wire the grep pattern above into CI or a pre-commit hook, so detection does not depend solely on manual review.
専門家のレビューが必要な作業
Consider recovering already-corrupted existing data
専門家レビュー必須Recovery is only possible if an independent source exists (external logs, a backup taken through a different path, etc.). If none exists, state clearly to stakeholders that recovery is impossible, and focus effort on preventing recurrence going forward.
!注意事項
- Adding the N prefix to new code does not automatically fix existing data that was already corrupted and stored.
- If the application layer builds SQL strings itself, using parameterized binding instead of string concatenation avoids this problem entirely. Prefer parameterization wherever possible.
- If test data is ASCII-only, the issue will not surface until production. Deliberately include non-ASCII characters (Japanese, Korean, emoji, etc.) in tests.
- Even after a full sweep fix, the old pattern can creep back in through a new code path or a sample SQL snippet pulled in from elsewhere. Keep the automated detection running continuously.
バージョン・環境による違い
これで解決しない場合に確認すること
Did you search across the whole codebase for the same pattern, not just one spot?
Fixing a single location does not rule out the same pattern existing in other stored procedures or batch jobs.
For columns suspected of corruption, does an independent source still exist?
If not, design future operations on the assumption that recovery is impossible.
Write paths that use parameterized queries are not affected by this issue in the first place
Confirm that paths using ORM or driver parameter binding are out of scope, so you prioritize correctly.
この文書の根拠と限界
製品の公式ドキュメントに基づく説明
That Unicode string literals require the N prefix is a specification explicitly documented by Microsoft for SQL Server. The "easy to miss" nature and tendency to recur described in this article are a generalization of patterns observed repeatedly across real operations, and do not include any specific customer's incident counts or dates.
よくある質問
Does forgetting the N prefix always cause an error?
No. It causes neither a compile-time nor a runtime error. Characters that cannot be represented in the connection's default code page are silently lost, while the operation itself completes normally.
Does this also happen outside of dynamic SQL?
Yes. Any path that assigns a string literal directly into an NVARCHAR column, variable, or parameter is affected the same way, even in a plain static INSERT statement.
Does using parameterized queries prevent this?
Yes. Paths that use a driver or ORM's parameter binding have the value's type and character encoding handled correctly on the driver side, so this problem cannot occur at all.
Can corrupted data be recovered?
Only if an independent source still exists — such as logs from an external system or records captured through a different path. The original character no longer exists inside the NVARCHAR column itself, so without such a source it cannot be recovered.
この文書がカバーする質問
- Why does Japanese or Korean text get mangled in SQL Server
- What happens with a SQL string that has no N prefix
リスク表示の意味
- 参照のみデータと設定を変更しません。
- 低影響は限定的ですが、権限と負荷の確認が必要です。
- 中性能・ロック・コストに影響する可能性があります。
- 高障害・データ損失・復旧作業が発生する可能性があります。
- 専門家レビュー必須本番適用前に別途レビューが必須です。
GIIPの対応範囲
GIIP has built a review-stage check that mechanically detects missing N prefixes for any newly added SQL code. Because the same class of incident has recurred, in different forms, across multiple projects, our policy is to never rely solely on manual review and to always pair it with automated detection.
執筆・技術検証
GIIP プロダクション運用チーム
大規模Webサービス、SQL Server、Oracle、AWS、Azureの設計・移行・運用に約30年従事。x12largeクラスのAWS RDS for SQL Server環境12セット、約12万テーブルのOracle環境、約3TBのTiDBからAurora MySQLへの移行を経験。現在も複数のクラウドデータベースと約30のWebサービスを、AIエージェントと人間の専門家が継続的に監視・運用しています。
What to Check When Migrating from utf8mb3 to utf8mb4 in Aurora MySQL
The first thing that breaks when converting to utf8mb4 is index key length. This organizes the SQL for finding targets, how to choose a collation, the impact of conversion, and where the settings live.
ai-operationsA guard that detects contamination in AI-generated content, and an operating rule that never treats regeneration alone as "done"
This explains an operating guard that mechanically detects known contamination patterns in AI-generated content and never calls a regeneration "done" until someone has directly checked the resulting body text.
incident-responseWhat database incidents AI agents can handle, and what humans must decide
We split incident response into phases to separate what AI can own (detection, triage, limited initial response) from what requires human judgment (irreversible actions, shutdown decisions), and provide read-only triage queries.
関連サービス
Request a sweep audit of NVARCHAR literals
同じ確認を複数の環境で継続する必要がある場合は、運用体制ごと相談できます。
Request a sweep audit of NVARCHAR literals