はじめに
最近、SQL ServerでWebアプリを開発しているのですが、DB側の処理でユニコードエスケープする必要が出てきました。
しかし、SQL Serverにはユニコードエスケープする関数が用意されておらず自作することになりました。
前提
SQL ServerはUnicode文字列を保存できるデータ型や照合順序が決まっています。
もしUnicode文字列が「???」となってしまう場合は、データ型や照合順序を確認してみてください。
実装
Unicodeエスケープの仕組み(処理の内容)に関しては、参考となるページがたくさんあるので調べてみてください。
ここでは省略させてください。
今回使ったSQL Serverの関数は以下の通り
- COLLATE:https://docs.microsoft.com/ja-jp/sql/t-sql/statements/collations?view=sql-server-2017
データベースもしくはテーブルカラムの照合順序をキャストしてくれる関数です。 - SUBSTRING:https://docs.microsoft.com/ja-jp/sql/t-sql/functions/substring-transact-sql?view=sql-server-2017
文字列を切り出す関数です。ほかの言語でも同様の関数が良く出てきますね。 - CONCAT:https://docs.microsoft.com/ja-jp/sql/t-sql/functions/concat-transact-sql?view=sql-server-2017
文字列を結合する関数です。これもよく出てきますね。 - CAST:https://docs.microsoft.com/ja-jp/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-2017
データ型をキャストする関数です。 - LOWER:https://docs.microsoft.com/ja-jp/sql/t-sql/functions/lower-transact-sql?view=sql-server-2017
大文字を小文字に変換する関数です。
実際の処理は下記です。
DECLARE @target nvarchar(100) = N'対象文字1字'
DECLARE @binary int;
SET @binary = UNICODE(@target COLLATE Japanese_Bushu_Kakusu_100_CI_AS_SC);
SELECT
CASE
WHEN @binary >= 0x0020 AND @binary <= 0x0020 THEN @target
WHEN @binary >= 0x0023 AND @binary <= 0x005B THEN @target
WHEN @binary >= 0x005D AND @binary <= 0x007A THEN @target
WHEN @binary > 0xFFFF THEN CONCAT('\u',LOWER(CONVERT(VARCHAR(8), CAST((@binary - 0x10000) / 0x0400 + 0xd800 AS VARBINARY(2)), 2)),
CONCAT('\u',LOWER(CONVERT(VARCHAR(8), CAST((@binary - 0x10000) % 0x0400 + 0xdc00 AS VARBINARY(2)), 2))))
ELSE CONCAT('\u', LOWER(CONVERT(VARCHAR(8), CAST(@binary AS VARBINARY(2)), 2)))
END
ユニコードエスケープの処理自体が、言語によってまちまちなので、CASE文の分岐に関しては、もう少しパターンがあるかもしれません。
また、今回はアルファベットや半角数字などエスケープする必要がないものはエスケープ対象外としています。
さいごに
今回、ユニコードエスケープをSQL Serverの処理だけで実現するために、結構苦労しました。
検索してみても、あまり同じようなことをしようとしている人がいないので、そもそもこういう設計になっているのがおかしいのかもしれません。