以下のようなテーブルがあります
CREATE TABLE dbo.DemoTable
(
Value VARCHAR(12)
)
Value
が次のパターンに一致する行のみを含むように制約したい
[axyto0-9\s]{0,2}[\s0-9]{0,10}
SQL Serverでこれを行うにはどうすればよいですか? (SOに関するこの質問に触発された)
TSQLでの文字列処理は不十分です。
PATINDEX
はワイルドカードの限定されたセットをサポートしますが、空白または数量詞に一致する_\s
_などの文字クラスはサポートしません。ネイティブTSQL式を使用して、この比較的単純な正規表現の検証を行うことは不可能ではありません。最大長12はデータ型によって強制され、varchar
を使用すると、コードページに空白文字が6つしかありません。
したがって、次のアプローチが機能するはずです
NOT LIKE CONCAT('%[^aotxy0123456789',char(9),char(10),char(11),char(12),char(13),char(32),char(160),']%')
と一致する必要がありますNOT LIKE CONCAT('%[^0123456789',char(9),char(10),char(11),char(12),char(13),char(32),char(160),']%')
と一致する必要がありますこれはすでにかなり厄介であり(私がそれが現在正しいかどうかさえもわかりません)、より複雑なパターンでは完全に実行不可能になります。
正規表現のサポートはCLRを使用することで利用でき、 the Java言語拡張 によって可能になる可能性の1つとしても呼び出されます。
これにより、TSQL表面積が拡張され、正規表現、文字列処理、およびNLPサポートを含むユースケースがより適切に処理されます。
これらのテクノロジーでの正規表現の使用例は、Microsoftサイトですでに公開されています( [〜#〜] clr [〜#〜] 、 Java )
この回答は、これらがオプションではない場合(おそらく、ポリシーによって無効にされているか、Azure SQLデータベースを使用していて、これらが利用できない場合)の解決策を示しています。
これは XMLスキーマ正規表現 を利用します。
_CREATE XML SCHEMA COLLECTION dbo.PatternValidatorSchema
AS '<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="test">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="[axyto0-9\s]{0,2}[\s0-9]{0,10}"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:schema>';
_
_ALTER TABLE dbo.DemoTable
ADD CONSTRAINT CK_ValidateValueMatchesPattern
CHECK (CAST(ISNULL('<test>' + REPLACE(REPLACE(REPLACE(Value, '&','&'), '<','<'), '>','>') + '</test>','') AS XML(dbo.PatternValidatorSchema)) IS NOT NULL)
_
このアプローチは一般的な検証にはあまり柔軟性がありません。失敗するとエラーがスローされるため、セットベースの方法で良行と不良行を特定することはできませんが、単一の不良値が見つかった場合に挿入を中止するためです。それは正常に動作します。
チェック制約は、実際に価値のあるものをチェックしていません。障害が発生するとエラーがスローされるため、型付きXMLへの変換が呼び出されるようにします。
_INSERT INTO dbo.DemoTable
VALUES (''),
('ax1234567890'),
('to123456'),
('AX1234567890');
_
エラー
メッセージ6926、レベル16、状態1、行37
XML検証:無効な単純型の値: 'AX1234567890'。場所:/ *:test [1]
試行された最後の値は大文字なので式に違反しているため、挿入はブロックされました。問題のある値はエラーでわかりやすく表示されます(メッセージの残りの部分は混乱を引き起こす可能性があります)
残念なことに、潜在的にかなりのパフォーマンス上のペナルティがあります。私のマシンでTABLOCK
ヒントを使用して1000万の有効な値をテーブルに挿入すると、チェック制約なしで約2.6秒かかりました。
チェック制約で以下を呼び出すと、CLR関数でこれが12秒に増加しました
_using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.Text.RegularExpressions;
public partial class UserDefinedFunctions
{
private static readonly Regex regexObj = new Regex(@"\A[axyto0-9\s]{0,2}[\s0-9]{0,10}\z", RegexOptions.Compiled);
[SqlFunction(DataAccess = DataAccessKind.None,
IsDeterministic = true,
IsPrecise = true,
SystemDataAccess = SystemDataAccessKind.None)]
public static SqlBoolean PatternValidator([SqlFacet(MaxSize = 12)]SqlString valueToCheck)
{
return new SqlBoolean(regexObj.IsMatch(valueToCheck.Value));
}
}
_
以下のチェック制約を設定すると、ネイティブTSQLの試行に17秒かかりました
_ALTER TABLE [dbo].[DemoTable] WITH CHECK ADD CONSTRAINT [CK_ValidateValueMatchesPattern] CHECK (
LEFT(Value,2) COLLATE Latin1_General_100_BIN2 NOT LIKE CONCAT('%[^aotxy0123456789',char(9),char(10),char(11),char(12),char(13),char(32),char(160),']%')
AND SUBSTRING(Value,3,10) COLLATE Latin1_General_100_BIN2 NOT LIKE CONCAT('%[^aotxy0123456789',char(9),char(10),char(11),char(12),char(13),char(32),char(160),']%')
)
_
タイプのXMLアプローチを使用すると、4分かかりました。
これは、1行あたり24マイクロ秒のオーダーであり、制約がないため、ペナルティです。そのため、これが支払う価値のある価格か、予想される挿入/更新の量を考慮に入れていないかを評価する必要があります。
比較のために、チェック制約を変更して型なしXMLを生成した後、その数の行を挿入するのに1分25秒かかったので、型付きXMLはそれにかなりのオーバーヘッドを追加します。