【问题标题】:SQL LIKE too slow identifying potential duplicatesSQL LIKE 识别潜在重复项的速度太慢
【发布时间】:2016-01-05 12:02:28
【问题描述】:

我正在尝试提高 Premises 表中数据的质量。

请看下面的 DDL(非常简化):

CREATE TABLE #Premises (ID int identity, Name varchar(100), primary key (ID))
INSERT INTO #Premises (name) values ('Tesco')
INSERT INTO #Premises (name) values ('Tesco Direct')

以及下面的 SQL:

select * from #Premises inner join #Premises as Premises2
ON #Premises.Name like '%' + Premises2.Name + '%'
where #Premises.ID<>Premises2.ID

这会返回一行,因为 Tesco Direct 就像 Tesco。但是,有数百万行,这显然很慢。该数据库安装在具有 SQL Enterprise 版本的服务器上。在这种情况下,全文搜索会有所帮助吗?

【问题讨论】:

  • 是的。 LIKE 会很慢。
  • 使用 PATINDEX 可能会快一点,但无论如何字符串操作并不是性能最好的操作。
  • 恕我直言,在考虑速度之前,您需要找到一种能够找到正确“欺骗”的算法。你使用like的方式不是很好。如果第一条记录有“TescoDirect”或“Tesco Direct”或只是“Tes”怎么办。就像会很慢,我认为全文也无济于事(soundex() ...函数都没有)。也许你应该想到一些 CLR 函数 - SQL server 本身在字符串操作方面很慢。
  • 您是否认为“Tesco Direct”和“XYZ Tesco”也重复
  • 这基本上是一个评估数十亿组合的笛卡尔连接。

标签: sql sql-server tsql


【解决方案1】:

关系数据库通过简单的评估获得索引和键之间直接匹配的惊人速度。

您实际上是在尝试对大量数据进行大规模(而不是简单)字符串处理。我还对懒人从大型数据库中输入的数据进行了清理,这很痛苦。

你的问题可能会变得很困难,你需要从中找到欺骗; “zip”、“邮政编码”、“Z.I.P.”、“k9”、“K-9”、“Canine”、“potatos”、“potatoes”...

FTS 可以提供帮助,但它的实用性取决于您想要什么(调整起来很痛苦)。

没有简单的方法来清理您的数据。请记住以渐进的方式执行此操作,您可以通过几个小步骤来完成它。

您可以做的一件事是实现一个 TAGS 表、一个包含文本字段中所有单词的字典以及一个将每个单词与其出现相关联的表。这样你就可以分析你可能的欺骗并瞄准它。 此外,创建此解决方案将花费时间并消耗您的服务器资源,以至于您无法在维护时段内完成这项工作。

【讨论】:

    【解决方案2】:

    如果您对 2 个字段中的数据没有任何控制权,您可以尝试像分隔字符串一样解析名称字段。这是一些您可以尝试的代码。

    CREATE FUNCTION [dbo].[fnSplitString] 
    ( 
        @string nvarchar(MAX), 
        @delimiter char(1) 
    ) 
    RETURNS @output TABLE(splitdata nvarchar(MAX) 
    ) 
    BEGIN 
        DECLARE @start int, @end int 
        SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 
        WHILE @start < LEN(@string) + 1 
        BEGIN 
            IF @end = 0  
                SET @end = LEN(@string) + 1
    
            INSERT INTO @output (splitdata)  
            VALUES(SUBSTRING(@string, @start, @end - @start)) 
            SET @start = @end + 1 
            SET @end = CHARINDEX(@delimiter, @string, @start)
    
        END 
        RETURN 
    END
    
    GO
    
    CREATE TABLE #Premises (ID int identity, Name varchar(100), primary key (ID))
    INSERT INTO #Premises (name) values ('Tesco')
    INSERT INTO #Premises (name) values ('Tesco Direct')
    
    CREATE TABLE #tmp(Id int, Name varchar(100))
    
    INSERT INTO #tmp (Id, Name)
    SELECT Id, dbo.fnSplitString(Name, ' ')
    FROM #Premises
    
    CREATE INDEX idxName ON #tmp(Name)
    CREATE INDEX idxId ON #tmp(Name)
    
    SELECT * 
    FROM #tmp AS Premises
        INNER JOIN #tmp AS Premises2 ON Premises.Name = Premises2.Name
    WHERE Premises.ID <> Premises2.ID
    

    此代码仅适用于匹配单个单词。它不适用于多个单词组合。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-01
    • 1970-01-01
    • 2018-03-14
    • 2017-02-28
    • 2023-01-19
    • 2020-12-20
    相关资源
    最近更新 更多