如何查找字符串的一部分是否出现多次 SQL SERVER

How to find if part of string appears more than one time SQL SERVER

我在SQL服务器上有任务要做。我有一个 table 包含 2 列(ID,Sortkey),看起来像这样。

ID        Sortkey
1         00
2         01
3         0101
4         0102
5         02
6         03
7         0301
8         030101
9         04
10        0401

我有一个任务,如果类似的字符串开头出现在table中,则在字符串前面写'+',如果没有,则写'-'。

输出应如下所示:

ID         Sortkey
1          -00
2          +01
3          -0101
4          -0102
5          -02
6          +03
7          +0301
8          -030101
9          +04
10         -0401

我已经尝试使用 count(*),但不知道如何计算是否存在具有相似字符串部分的记录。我想象有两种可能性的解决方案,一种是搜索是否有任何字符串包含与我正在查看的整个字符串相同的部分并且 return + 在字符串前面, 而 else 会 return -.

非常感谢

你可以用一个OUTER APPLY来统计相似的行数,当计数大于0时使用+,否则使用-:

DECLARE @T TABLE (ID INT, SortKey VARCHAR(10));
INSERT @T (ID, SortKey)
VALUES
    (1, '00'), (2, '01'), (3, '0101'), (4, '0102'), (5, '02'),
    (6, '03'), (7, '0301'), (8, '030101'), (9, '04'), (10, '0401');

SELECT  T1.ID, SortKey = CASE WHEN d.SimilarKeys > 0 THEN '+' ELSE '-' END + T1.SortKey
FROM    @T AS T1
        OUTER APPLY
        (   SELECT  COUNT(*)
            FROM    @T AS T2
            WHERE   T2.SortKey LIKE T1.SortKey + '%'
            AND     T2.ID != T1.ID
        ) AS d (SimilarKeys);

性能测试

我在另一个答案上评论过性能,所以我认为最好至少包括我是如何测试它的:

IF OBJECT_ID(N'dbo.T', 'U') IS NOT NULL DROP TABLE dbo.T;
CREATE TABLE dbo.T(ID INT NOT NULL PRIMARY KEY, SortKey VARCHAR(10));
INSERT dbo.T (ID, SortKey)
SELECT  TOP 100000
        ROW_NUMBER() OVER(ORDER BY (SELECT NULL)), 
        RIGHT('0000' + CONVERT(VARCHAR(10), FLOOR(RAND(CHECKSUM(NEWID())) * 10000)), 
                CEILING(RAND(CHECKSUM(NEWID())) * 8))
FROM    sys.all_objects a
        CROSS JOIN sys.all_objects b;

我用于测试的查询是:

查询 1

SELECT  COUNT(CASE WHEN d.SimilarKeys > 0 THEN '+' ELSE '-' END + T1.SortKey)
FROM    dbo.T AS T1
        OUTER APPLY
        (   SELECT  COUNT(*)
            FROM    dbo.T AS T2
            WHERE   T2.SortKey LIKE T1.SortKey + '%'
            AND     T2.ID != T1.ID
        ) AS d (SimilarKeys);

查询 2

WITH cte AS
(
    SELECT  t1.ID, t1.Sortkey, 
            SUM(CASE WHEN  t2.sortkey like t1.sortkey + '%' THEN 1 ELSE 0 END) 
                OVER (PARTITION BY t1.ID) AS ContainsCount,
            ROW_NUMBER() OVER (PARTITION BY t1.ID ORDER BY t1.id) AS rnr
    FROM    dbo.T AS t1
            LEFT JOIN dbo.T AS t2 
                ON t1.ID <> t2.ID
)
SELECT  COUNT(CASE WHEN ContainsCount > 0 THEN '+' ELSE '-' END + Sortkey) AS Sortkey 
FROM    cte 
WHERE   rnr  = 1;    

我在 3 分钟后放弃了 运行 两个查询,两个查询都没有执行得很好,这并没有让我感到惊讶,因为它需要 100,000 个嵌套循环,每个循环搜索 100,000 行,没有索引。所以我在 table:

中添加了一个索引
CREATE NONCLUSTERED INDEX IX_T__SortKey ON dbo.T (SortKey);

在这种情况下我每次查询1 运行大约需要14秒运行它,我这次在10分钟后放弃了运行查询2。我将 table 中的行数减少到 1,000,查询 2 最终 运行 完成(8 秒),很明显为什么它在查看 IO 时表现如此糟糕:

查询 1

Table 'T'. Scan count 1001, logical reads 2031

查询 2

Table 'Worktable'. Scan count 15, logical reads 2061426 Table 'Worktable'. Scan count 0, logical reads 0 Table 'T'. Scan count 8, logical reads 25

因此查询 2 仅需要 1,000 条记录的 200 万次读取,这解释了性能缓慢的原因。

这 Sql 在大表上可能不是最有效的,但它产生了预期的结果。

  SELECT CASE WHEN (SELECT COUNT(*) FROM Table1 T2 WHERE T2.SortKey LIKE T1.SortKey + '%') > 1 THEN '-' ELSE '+' END + T1.SortKey AS SortKey
  FROM Table1 T1

你可以尝试使用子select

Update t1
set sortkey = CONCAT(
  (CASE WHEN (
               SELECT count(*) 
               from @table t2 
               where t2.SortKey like Concat(t1.SortKey, '%')
             ) > 1 
  THEN '+'
  ELSE '-'
  END)
  , sortKey) 
from @table t1

基本思想是,您正在计算所有记录,您正在计算 SortKey 类似于 SortKey%

的所有行

这意味着如果您有两行具有相同的排序键,那么它们都将获得 +

如果你想避免这种情况,你可以

and t2.sortkey <> t1.sortkey

到 select 语句中 where 的末尾