【问题标题】:Problem with ORs in JOIN statementJOIN 语句中的 OR 问题
【发布时间】:2009-08-30 02:17:20
【问题描述】:

我对下面详述的 SQL 语句有疑问。该查询返回我需要的结果,但执行需要大量时间。我现在在数据库中有很多记录,页面通常不会加载。

SELECT dscan.guid, dscan.drive, dscan.folder, dscan.filename, source.guid  
FROM source 
RIGHT JOIN dscan ON (
  (source.guid & '_dtr' = dscan.guid OR source.guid & '_dto' = dscan.guid OR source.guid = dscan.guid)  
  AND dscan.guid LIKE '%" & Replace(strSearch_guid, "'", "''") & "%'  
  AND dscan.filename NOT LIKE '.[_]%'  
  AND dscan.drive = 'Z:')  
WHERE source.guid Is Null  
ORDER BY dscan.drive, dscan.guid

根据我在网上找到的信息,JOIN 语句中的 OR 是一个问题,但我不知道如何解决这个问题。

我将数据库记录与文件名进行比较以识别错误 - 但文件名有时具有我必须考虑的“_dtr”或“_dto”附加信息。

【问题讨论】:

  • 您确定问题出在ORs 上吗?如果你把它减少到只有一个测试,它运行得很快吗?另外:这些表上定义了哪些索引?
  • 什么数据库和版本?
  • 我很肯定 OR 是问题所在。如果我删除它们,SQL 执行得非常快。 source.guid 已编入索引,但 dscan.guid 未编入索引(此表受到相当频繁的大规模更新)。我曾尝试索引它,但它没有任何区别。从我在别处读到的内容来看,连接中的 OR 将导致索引被忽略。 DB 是 MS Access。

标签: sql performance join


【解决方案1】:

您使用构造的谓词比较值以及开头带有通配符的“Like”将需要完整的表扫描。在您重新设计架构以消除这种情况之前,这将是大型表的主要性能损失。但是,您可以通过联合三个单独的 sql 语句来消除 OR 对性能的影响。试试这个:

    SELECT D.guid, D.drive, D.folder, D.filename, S.guid  
    FROM dscan D Left Join source S
        ON S.guid & '_dtr' = D.guid 
          AND D.guid LIKE '%" & Replace(strSearch_guid, "'", "''") & "%'   
          AND D.filename NOT LIKE '.[_]%'    
          AND D.drive = 'Z:')  
    WHERE S.guid Is Null  
  Union
    SELECT D.guid, D.drive, D.folder, D.filename, S.guid  
    FROM dscan D Left Join source S
        ON S.guid & '_dto' = D.guid  
          AND D.guid LIKE '%" & Replace(strSearch_guid, "'", "''") & "%'   
          AND D.filename NOT LIKE '.[_]%'    
          AND D.drive = 'Z:')  
    WHERE S.guid Is Null  
  Union
    SELECT D.guid, D.drive, D.folder, D.filename, S.guid  
    FROM dscan D Left Join source S
        ON S.guid = D.guid    
          AND D.guid LIKE '%" & Replace(strSearch_guid, "'", "''") & "%'   
          AND D.filename NOT LIKE '.[_]%'    
          AND D.drive = 'Z:')  
    WHERE S.guid Is Null  
    ORDER BY D.drive, D.guid

【讨论】:

  • union 开始了一个独特的。如果这些表真的很大,它们将返回大量结果,并且必须整理并消除所有重复的nulls。我无法想象这在任何层面上都会比 in 更快,我之前测试过的 SQL Server 也不能。
  • 我想建立一个工会,并尽我最大的努力提出这个查询。问题是这个查询将返回太多的记录,因为对于任何给定的记录,即使在一个查询中找到匹配项(意味着它因为 source.guid 为空而被排除),它也必然会被另一个查询返回两个查询,因为 dscan.guid 不能同时 = source.guid, source.guid &'_dto' 和 source.guid & '_dtr'
  • @Eric,正如尼克所说,Union 启动了 Distinct,因此应排除这些重复项。如果您看到不同的行为,则输出中还有其他不同的列。 @Nick,理论上我同意你的观点,我并不完全理解查询处理器对联合的作用,但在实践中(正如我所说)我已经看到这比在 Where 子句谓词中使用 OR 产生更好的结果。
【解决方案2】:

事实上,我不认为您的性能问题来自“ORs”,而主要是因为您使用将字符串与列值连接来进行连接。

同样加入字符串数据并不能提供最佳性能。另一方面,如果你的列被索引(在它们上添加一个索引)它会有所帮助(如果你没有再次连接字符串)

作为一种解决方案,我不知道是否可以或想要向该表添加列并包含已添加“扩展”的字符串版本,因此查询不需要连接它们?

这些只是一些变通方法,而不是真正的解决方案

【讨论】:

  • 在 dscan 表中添加一列是我的备份选项。不过,SQL 解决方案会为我省去一些麻烦。
  • 感谢所有试图帮助我解决这个问题的人。我希望有一个 SQL 灵丹妙药,但我认为没有。我选择在 dscan 表中添加一个包含“_dtr”和“_dto”数据的列,这样我就可以直接使用 source.guid = dscan.guid 运行查询。
【解决方案3】:

我认为你应该试着用括号来剖析一下。

((source.guid & '_dtr' = dscan.guid) OR (source.guid & '_dto' = dscan.guid) OR (source.guid = dscan.guid))

【讨论】:

  • 出于绝望,我玩弄了这样的括号,但没有任何区别。问题在于 OR,正如 Charles Bretana 所说,它强制进行表扫描。
【解决方案4】:

ors 是臭名昭著的性能傻瓜。尝试改用in 子句。:

SELECT dscan.guid, dscan.drive, dscan.folder, dscan.filename, source.guid  
FROM source 
RIGHT JOIN dscan ON (
  dscan.guid in (source.guid & '_dtr', source.guid & '_dto', source.guid)
  AND dscan.guid LIKE '%" & Replace(strSearch_guid, "'", "''") & "%'  
  AND dscan.filename NOT LIKE '.[_]%'  
  AND dscan.drive = 'Z:')  
WHERE source.guid Is Null  
ORDER BY dscan.drive, dscan.guid

但是,最好还是使用您的查询执行计划来真正了解数据库引擎在做什么。然后,您可以查看真正的瓶颈在哪里,以及您可以添加哪些索引来加快查询速度。

【讨论】:

  • 我尝试过 IN 但结果相同 - 查询执行时间过长,页面无法加载。
【解决方案5】:

也许你可以重新订购东西。你可以试试:

  • 首先从 dscan 中选择(我假设 dscan 上的正确连接意味着您想要其中的所有行)。之后您甚至可能不需要正确的加入。
  • 重新排序您的 ON 子句,例如。将最有可能失败的比较放在首位 - 以利用短路。将所有 AND 放在首位,OR 放在最后
  • 将一些比较从 ON 子句移到 WHERE 子句

【讨论】:

    【解决方案6】:

    我重写了你的查询:

     SELECT d.guid, 
            d.drive, 
            d.folder, 
            d.filename,
            src.guid
       FROM DSCAN d
       JOIN (SELECT s.quid,
                    s.quid & '_dtr' AS DTR,
                    s.quid & '_dto' AS DTO,
               FROM SOURCE s
              WHERE s.guid IS NOT NULL) src ON d.guid IN (s.quid, s.dtr, s.dto)
       WHERE d.guid LIKE '%' & REPLACE(strSearch_guid, "'", "''") & '%'
         AND d.filename NOT LIKE '.[_]%'  
         AND d.drive = 'Z:'
    ORDER BY d.drive, d.guid
    

    我假设您在 OP 中有一个关于 source.guid IS NULL 的类型 - 您只想要 NULL source.guid 记录然后连接到它们上是没有意义的。

    这个:

    RIGHT JOIN dscan ON (source.guid & '_dtr' = dscan.guid OR 
                         source.guid & '_dto' = dscan.guid OR 
                         source.guid = dscan.guid)
    

    ...将仅使用 guid 列上的索引(假设存在一个)来创建值,用于比较。如果您需要这样做,最好在内联视图或 CTE/子查询分解中构造它们。

    【讨论】:

    • source.guid IS NULL 在我的查询中位于正确的位置,因为我只想要 dscan 表中与源表不匹配的记录。我已经尝试使用 IN 代替 OR,但结果同样糟糕。
    【解决方案7】:

    现在让我们看看你的 select 语句的开始:

    选择 dscan.guid、dscan.drive、dscan.folder、dscan.filename、source.guid
    来源 右连接 dscan 开启

    并以此结束:

    source.guid 为空
    由 dscan.drive、dscan.guid 订购

    因此,如果我正确阅读了此内容,您将尝试从 dscan 表中读取所有内容以及与源表匹配的内容。

    ??如果您查找的 source.guid 为空,究竟是什么链接了这两个表?因为如果 source.guid 为空,你为什么要这样做:

    来源 右连接 dscan ON ( (source.guid & '_dtr' = dscan.guid OR source.guid & '_dto' = dscan.guid OR source.guid = dscan.guid)
    AND dscan.guid LIKE '%" & Replace(strSearch_guid, "'", "''") & "%'
    AND dscan.filename NOT LIKE '.[_]%'
    AND dscan.drive = 'Z:')

    您的查询需要很长时间,因为它在连接中丢失了。你的尝试 让它过滤太多的东西并过滤像 '% blah blah %' 不 帮助速度。检查源表和 dscan 表上是否有正确的索引。

    为什么 WHERE source.guid 为 NULL 您需要 source.guid 与 dscan 表中的 dscan.guide 匹配。

    【讨论】:

    • source.guid的点为NULL是为了标识dscan表中与源表中没有匹配的记录。其他过滤器不是问题的原因——肯定是那些 OR——它们是获得正确结果所必需的。
    猜你喜欢
    • 1970-01-01
    • 2012-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多