Mar*_*ith 13 sql-server regular-expression check-constraints
我有一张如下表
CREATE TABLE dbo.DemoTable
(
Value VARCHAR(12)
)
Run Code Online (Sandbox Code Playgroud)
我想限制它只包含Value匹配以下模式的行
[axyto0-9\s]{0,2}[\s0-9]{0,10}
如何在 SQL Server 中执行此操作?(受 SO 上这个问题的启发)
Mar*_*ith 23
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 获得,也被称为Java 语言扩展启用的可能性之一。
这扩展了 TSQL 的表面积,以更好地处理涉及正则表达式、字符串处理和 NLP 支持的用例。
Microsoft 站点上已经提供了将正则表达式与这些技术结合使用的示例(CLR、Java)
This answer为这些不是一个选项的情况提供了解决方案(可能被策略禁用或使用这些不可用的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>';
Run Code Online (Sandbox Code Playgroud)
ALTER TABLE dbo.DemoTable
ADD CONSTRAINT CK_ValidateValueMatchesPattern
CHECK (CAST(ISNULL('<test>' + REPLACE(REPLACE(REPLACE(Value, '&','&'), '<','<'), '>','>') + '</test>','') AS XML(dbo.PatternValidatorSchema)) IS NOT NULL)
Run Code Online (Sandbox Code Playgroud)
这种方法对于一般验证不是很灵活,因为任何失败都会引发错误,因此它不能用于以基于集合的方式确定好行和坏行,但为了在发现单个错误值时中止插入它工作正常。
检查约束并不是真正检查任何有价值的东西。它会导致调用到类型化 XML 的转换,因为任何失败都会导致抛出错误。
INSERT INTO dbo.DemoTable
VALUES (''),
('ax1234567890'),
('to123456'),
('AX1234567890');
Run Code Online (Sandbox Code Playgroud)
错误
Msg 6926, Level 16, State 1, Line 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));
}
}
Run Code Online (Sandbox Code Playgroud)
使用以下检查约束,本机 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),']%')
)
Run Code Online (Sandbox Code Playgroud)
使用类型化 XML 方法需要 4 分钟。
这是每行 24 微秒的惩罚,而不是没有约束,因此需要评估这是否值得支付或不考虑预期的插入/更新量。
仅通过更改检查约束以生成非类型化 XML 后的比较,插入该数量的行需要 1 分 25 秒,因此类型化 XML 确实增加了一些显着的开销。