Sql 将列值匹配为前缀
我正在SQL Server表中存储字符串前缀,我想看看这些值中是否有一个是给定参数值的有效前缀 e、 g.假设我有一个电话禁止通话列表,其中包含一个条目,禁止所有电话呼叫以“Sql 将列值匹配为前缀,sql,sql-server,Sql,Sql Server,我正在SQL Server表中存储字符串前缀,我想看看这些值中是否有一个是给定参数值的有效前缀 e、 g.假设我有一个电话禁止通话列表,其中包含一个条目,禁止所有电话呼叫以“1425123”开头的号码,而不是插入10000个号码(14251230000到14251239999),它会存储前缀 像这样: CREATE TABLE Prefixes ( Value varchar(10) ) CREATE INDEX IX_Value UNIQUE Prefixes ( Value )
1425123
”开头的号码,而不是插入10000个号码(14251230000
到14251239999
),它会存储前缀
像这样:
CREATE TABLE Prefixes (
Value varchar(10)
)
CREATE INDEX IX_Value UNIQUE Prefixes ( Value )
这样评价:
DECLARE @value varchar(10) = 'foobar'
SELECT
*
FROM
Prefixes
WHERE
@value LIKE ( Value + '%' );
当我在SQLServerManagementStudio中的Azure SQL中运行它时,它表示正在执行索引扫描。Azure SQL S1数据库上大约有70000个条目,执行查询需要200到500毫秒。该工具不建议对索引进行任何改进以提高性能
为了进行比较,精确相等匹配(Value=@Value
)使用索引查找,并且几乎立即发生
200-500毫秒对于我的应用程序来说太慢了
一种方法是使用Trie将查找移动到我的应用程序代码中,以实现高效的前缀搜索(这会带来同步问题),但另一种方法是将查询更改为如下内容:
DECLARE @v1 varchar(1) = LEFT( @value, 1 )
DECLARE @v2 varchar(2) = LEFT( @value, 2 )
DECLARE @v3 varchar(3) = LEFT( @value, 3 )
DECLARE @v4 varchar(4) = LEFT( @value, 4 )
DECLARE @v5 varchar(5) = LEFT( @value, 5 )
DECLARE @v6 varchar(6) = LEFT( @value, 6 )
DECLARE @v7 varchar(7) = LEFT( @value, 7 )
DECLARE @v8 varchar(8) = LEFT( @value, 8 )
DECLARE @v9 varchar(9) = LEFT( @value, 9 )
SELECT
*
FROM
Prefixes
WHERE
Value = @v1 OR
Value = @v2 OR
Value = @v3 OR
Value = @v4 OR
Value = @v5 OR
Value = @v6 OR
Value = @v7 OR
Value = @v8 OR
Value = @v9
当我运行这个程序时,速度要快得多(使用索引搜索),但感觉像是黑客攻击,但因为我知道长度小于10个字符,所以我可以接受它。。。现在
有更好的办法吗?SQL Server是否有一种方法可以在内部进行前缀匹配(即,在我的最后一个示例中使用相同的逻辑,但不使用重复且脆弱的SQL)?第一个选项之所以慢,是因为它不是,因为您正在修改where子句中的
前缀.Value
因此,不可能利用该指数
您建议的解决方案很好(尽管缺少长度为10的前缀)
我唯一要指出的是,您肯定更愿意使用EXISTS
查询?一旦你找到了一个匹配项,那么你就完成了;不需要再找了。而且中的更简洁
即
如果这真的很重要,你可以考虑使用。(不幸的是,我自己从未使用过它,因此无法进一步帮助。)我知道这需要更多的工作,但可能是合理的。它过去需要运行额外的服务;我不知道情况是否仍然如此
编辑
在以下情况下,从中借款仍然是低效的:
@Value='9999999'
并且不匹配任何前缀
- 原因是所有
前缀.Value<'99999999'
- 但是没有一个与筛选器
@值匹配,比如value+'%'
- 因此,查询仍然必须扫描所有行
不过,我确实认为(通过一些调整)可以通过始终首先获取值
,然后检查该值是否与@value like value+'%'
特别匹配来提高效率。问题是,您需要首先保证前缀
不包含任何“冗余”值(或者至少可以使用标志轻松过滤掉冗余值)
我所说的冗余是指任何本身无效的值,因为它以现有的较短前缀开头
然后可以使用以下查询
SELECT *
FROM (
SELECT TOP 1 Value as PossiblePrefix
FROM Prefixes
/* WHERE can leverage index;
but requires NO redundant Prefixes.Value rows
so that it returns only ONE possible prefix that
has a chance of matching @Value.*/
WHERE Value <= @Value
ORDER BY Value DESC
) pp
WHERE @Value LIKE pp.PosisblePrefix + '%'
选择*
从(
选择TOP 1值作为可能的Prefix
来自前缀
/*哪里可以利用指数;
但不需要冗余前缀。值行
所以它只返回一个可能的前缀
有机会匹配@Value*/
其中Value这是一个辅助数字表可以帮助的
因为您只需要1-10
,所以我在查询中内联了一个,而不是假设存在一个
如果您有或可以创建一个永久数字表,则可以通过将派生表V
替换为对永久数字表的引用来缩短代码
SELECT IIF(EXISTS (SELECT *
FROM (VALUES(1),(2),(3),
(4),(5),(6),
(7),(8),(9),(10)
) V(number)
JOIN Prefixes P WITH(FORCESEEK)
ON P.Value = LEFT(@value, number)
WHERE number <= LEN(@value)), 1, 0) AS PrefixExists
选择IIF(存在)(选择*
从(值(1)、(2)、(3),
(4),(5),(6),
(7),(8),(9),(10)
)V(数字)
用(FORCESEEK)连接前缀P
在P.值=左(@Value,number)
其中数字是70000个条目,这是真实的吗?其中有多少是完整的数字,有多少是前缀。还有多少条目是多余的?例如,如果前缀包含'1'
,那么可能会有数百个甚至数千个以1
开头的条目被忽略,因为'1'
已经是'14251230000'
。很好!我是否可以建议使用set showplan\u text on
来获得比图像更有用的查询计划?
SELECT IIF(EXISTS (SELECT *
FROM (VALUES(1),(2),(3),
(4),(5),(6),
(7),(8),(9),(10)
) V(number)
JOIN Prefixes P WITH(FORCESEEK)
ON P.Value = LEFT(@value, number)
WHERE number <= LEN(@value)), 1, 0) AS PrefixExists
|--Compute Scalar(DEFINE:([Expr1014]=CASE WHEN [Expr1015] THEN (1) ELSE (0) END))
|--Nested Loops(Left Semi Join, DEFINE:([Expr1015] = [PROBE VALUE]))
|--Constant Scan
|--Nested Loops(Inner Join, OUTER REFERENCES:([Union1010]))
|--Filter(WHERE:([Union1010]<=len([@value])))
| |--Constant Scan(VALUES:(((1)),((2)),((3)),((4)),((5)),((6)),((7)),((8)),((9)),((10))))
|--Index Seek(OBJECT:([tempdb].[dbo].[Prefixes].[IX_Value] AS [P]), SEEK:([P].[Value]=substring([@value],(1),[Union1010])) ORDERED FORWARD)