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

时间:2015-08-24 07:12:39

标签: sql-server string-comparison

我在SQL Server中有一项任务。我有一个包含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

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

输出应如下所示:

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

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

非常感谢

3 个答案:

答案 0 :(得分:4)

您可以使用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;

我用于测试的查询是:

QUERY 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);

QUERY 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行没有索引。所以我在表格中添加了一个索引:

CREATE NONCLUSTERED INDEX IX_T__SortKey ON dbo.T (SortKey);

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

查询1

  

表'T'。扫描计数1001,逻辑读取2031

查询2

  

表'工作台'。扫描计数15,逻辑读取2061426   表'工作台'。扫描计数0,逻辑读取0   表'T'。扫描计数8,逻辑读取25

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

答案 1 :(得分:1)

这个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

答案 2 :(得分:1)

您可以尝试使用子选择

[Key, Column(Order = 2), ForeignKey("Product")]
public int ProductId { get; set; }
public virtual Product Product { get; set; }

基本思想是你计算所有记录的所有记录,其中SortKey就像SortKey%

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

如果你想避免可以和

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

到选择陈述中的位置

的末尾