【问题标题】:TSQL "LIKE" or Regular Expressions?TSQL“喜欢”或正则表达式?
【发布时间】:2009-01-19 18:21:54
【问题描述】:

我在一个表中有一堆 (750K) 记录,我必须看到它们在另一个表中。第二张表有几百万条记录,数据是这样的:

源表
9999-A1B-1234X,中间部分可能超过三位数

目标表
DescriptionPhrase9999-A1B-1234X(9 pages) - 是的,括号和单词在字段中。

目前我正在运行一个加载源记录的 .net 应用程序,然后运行并搜索类似(使用 tsql 函数)以确定是否有任何记录。如果是,则源表更新为正数。如果没有,则单独保留该记录。

该应用每小时处理大约 1000 条记录。当我在 sql server 上作为游标存储过程执行此操作时,我几乎得到了相同的速度。

如果正则表达式或任何其他方法可以使其更快,有什么想法吗?

【问题讨论】:

    标签: sql-server tsql


    【解决方案1】:

    如何在数据库中完成这一切,而不是将记录提取到您的 .Net 应用程序中:

    UPDATE source_table s SET some_field = true WHERE EXISTS
    (
         SELECT target_join_field FROM target_table t 
         WHERE t.target_join_field LIKE '%' + s.source_join_field + '%'
    )
    

    这会将查询总数从 750k 更新查询减少到 1 个更新。

    【讨论】:

      【解决方案2】:

      首先,如果可能的话,我会重新设计。最好添加一个包含正确值并能够加入的列。如果你还需要长的。您可以使用触发器在插入时将数据提取到列中。

      如果您有可以匹配的数据,那么您既不需要不能使用索引的“%somestuff%”,也不需要游标,这两者都是性能杀手。如果您设计得当,这应该是基于集合的任务。如果设计不好并且无法更改为好的设计,我看不出使用 t-SQl 获得良好性能的好方法,我会尝试正则表达式路线。不知道有多少不同的 prharses 和每个的结构,我不能说正则表达式路线是否容易甚至可能。但如果没有重新设计(我强烈建议你这样做),我看不到另一种可能性。

      顺便说一句,如果您正在处理这么大的表,我会下定决心不再编写另一个游标。它们对性能非常不利,尤其是当您开始使用这种大小的记录时。学会在集合中思考,而不是通过记录处理记录。

      【讨论】:

      • 请注意,他已经设法找到另一种技术,使其像光标一样慢。这至少是一种成就。我怀疑这是涉及 UDF 的搜索。
      【解决方案3】:

      使用单个更新(mbeckish 的回答)需要注意的一点是事务日志(如果查询被取消,则启用回滚)将会很大。这将大大减慢您的查询速度。因此,最好以 1,000 行或类似的块的形式处理它们。

      此外,条件(b.field like '%' + a.field + '%')将需要检查 b(百万)中的每条记录与 a(750,000)中的每条记录。这相当于超过 7500 亿次字符串比较。不太好。

      直觉“索引的东西”在这里也无济于事。索引使事情井井有条,因此第一个字符决定了索引中的位置,而不是您感兴趣的位置。

      第一个想法

      出于这个原因,我实际上会考虑创建另一个表,并将长/混乱的值解析成更好的东西。一个例子就是从最后一个 '(' 开始删除任何文本。(假设所有值都遵循该模式)这会将查询条件简化为 (b.field like '%' + a.field)

      尽管如此,索引在这里也无济于事,因为重要的字符在最后。因此,奇怪的是,以相反的顺序存储两个表的字符可能是值得的。然后你的临时表上的索引就会被使用。

      花费这么多时间可能看起来很浪费,但在这种情况下,小小的好处会产生更大的回报。 (例如,需要几个小时才能将比较从 7500 亿减少到 3750 亿。如果您可以使用索引,则可以将其减少一千倍,这要归功于索引是树搜索,而不仅仅是有序表......)

      第二个想法

      假设您确实将目标表复制到临时表中,您可能会从以 1000 块为单位处理它们中受益,方法是从目标表中删除匹配的记录。 (这仅在您从目标表中删除有意义的数量时才值得。这样,在检查了所有 750,000 条记录之后,目标表现在 [例如] 是其开始大小的一半。)

      编辑:
      修改后的第二个想法

      1. 将整个目标表放入临时表中。

      2. 尽可能对值进行预处理,以使字符串比较更快,甚至引入索引。

      3. 一次循环遍历源表中的每条记录。在循环中使用以下逻辑...

        删除目标 WHERE 字段 LIKE '%' + @source_field + '%' 如果(@@row_count = 0) [无匹配] 别的 [匹配]

      连续删除使每个循环的查询速度更快,并且您只需对数据使用一次查询(而不是一次查找匹配项,第二次删除匹配项)

      【讨论】:

        【解决方案4】:

        试试这个 --

        update SourceTable  
        set ContainsBit = 1  
        from SourceTable t1      
          join (select TargetField from dbo.TargetTable t2) t2   
            on charindex(t1.SourceField, t2.TargetField) > 0
        

        【讨论】:

        • 仍然很慢,但比游标或通过客户端应用程序路由要慢得多。
        【解决方案5】:

        首先要确保您在搜索表中具有该列的索引。其次是在左侧做 LIKE without % 符号。检查执行计划,看看您是否没有对每一行进行表扫描。

        正如 le dorfier 正确指出的那样,如果您使用的是 UDF,那么希望渺茫。

        【讨论】:

        • 他们正在使用 TSQL 函数,仅此一项就无法进行优化。即使是表扫描也会比游标好几个数量级。
        【解决方案6】:

        给猫剥皮的方法有很多——我认为首先要知道这是一次性操作,还是需要定期完成的常规任务。

        不知道你问题的所有细节,如果是我,这是一次性的(或不经常的操作,听起来像是),我可能会从两者中提取相关字段包括源表中的主键的表,并将它们作为文本文件导出到本地计算机。文件大小可能会明显小于数据库中的完整表。

        我会使用用“C”/C++ 或其他具有原始处理能力的“轻量级”语言编写的例程在快速机器上本地运行它,并写出“匹配”的主键表,然后我会将其加载回 sql server 并将其用作更新查询的基础(即更新源表,其中 id in select id from temp table)。

        您可能会花费几个小时来编写例程,但它的运行时间只是您在 sql 中看到的时间的一小部分。

        根据您的 sql 声音,您可能正在尝试对数百万条记录表进行 750,000 次表扫描。

        告诉我们更多关于这个问题的信息。

        【讨论】:

          【解决方案7】:

          神圣的烟雾,多么棒的反应!

          系统处于断网状态,所以我无法复制粘贴,但这是重新输入

          当前 UDF:

          Create function CountInTrim 
          (@caseno varchar255)
           returns int
          as 
          Begin
          declare @reccount int
          select @reccount = count(recId) from targettable where title like '%' + @caseNo +'%'
          return @reccount
          end
          

          基本上,如果有记录计数,则存在匹配,.net 应用程序会更新记录。基于游标的存储过程具有相同的逻辑。

          此外,这是一个一次性的过程,确定旧记录/案例管理系统中的哪些条目成功迁移到新系统中,因此我无法重新设计任何东西。当然,这两个系统的开发人员都已经没有了,虽然我有一些 sql 经验,但我绝不是专家。

          我从旧系统必须创建源表的疯狂方式中解析案例编号,而这是与新系统唯一的共同点,即案例编号格式。我可以尝试解析新系统中的案例编号,然后针对这两组运行匹配,但可能会使用一组数据,例如:

          DescriptionPhrase1999-A1C-12345(5 pages)
          Phrase/Two2000-A1C2F-5432S(27 Pages)
          DescPhraseThree2002-B2B-2345R(8 pages)
          

          解析变得有点复杂,所以我想我会让它变得更简单。

          我将尝试单个更新语句,然后在需要时回退到 clr 中的正则表达式。

          我会更新结果。而且,由于我已经处理了一半以上的记录,这应该会有所帮助。

          【讨论】:

          • 我现在要做的是继续做你已经开始的事情——迭代各种近似值,每个近似值都捕获更多记录。
          【解决方案8】:

          从上面尝试 Dan R 的更新查询:

          update SourceTable  
          set ContainsBit = 1  
          from SourceTable t1      
           join (select TargetField 
                 from dbo.TargetTable t2) t2   
           on charindex(t1.SourceField, t2.TargetField) > 0
          

          或者,如果及时性很重要并且这是 sql 2005 或更高版本,那么这将是使用带有正则表达式的 SQL CLR 代码的计算列的经典用途 - 不需要独立的应用程序。

          【讨论】:

            猜你喜欢
            • 2011-03-16
            • 1970-01-01
            • 1970-01-01
            • 2013-05-14
            • 1970-01-01
            • 2012-09-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多