【问题标题】:Microsoft SQL Server performance- or in on clause [duplicate]Microsoft SQL Server 性能-or in on 子句 [重复]
【发布时间】:2015-11-12 19:01:38
【问题描述】:

我需要使用第二个表中的字段“cdi”与第一个表中的 cdi 或 cd_cliente 连接 2 个表。我的意思是它可能匹配第一个表中的相同字段或 cd_cliente。

我原来的查询是

select 
    a.cd_cliente, a.cdi as cdi_cli,b.* 
from 
    clientes a 
left join 
    rightTable b on a.cdi = b.cdi or a.cd_cliente = b.cdi

但是因为太费时间,我改成:

Select a.cd_cliente, a.cdi, b.* 
from clientes a
left join
    (select 
         a.cd_cliente, a.cdi as cdi_cli, b.* 
     from 
         clientes a 
     inner join 
         rightTable  b on a.cdi = b.cdi 
     union 
     select 
         a.cd_cliente, a.cdi as cdi_cli, b.* 
     from 
         clientes a 
     inner join 
         rightTable  b on a.cd_cliente = b.cdi) b
      on a.cd_cliente=b.cd_cliente

而且花费的时间更少。我不确定结果是否相同。如果是这样,为什么第二次查询所花费的时间要少得多?

【问题讨论】:

  • 您需要发布执行计划和索引以便我们帮助您。我的猜测是第一个使用表扫描,而两个中的一个是一个巨大的表,因此需要很多时间
  • @hatchet,不是重复的。链接的问题有两个意思相同的陈述。在这个问题中,这两个陈述是不等价的。
  • 首先使用COUNT()比较两个结果,看看是否返回相同的行数。这样我们就可以确定是否返回相同的结果。还请包括您的执行计划。 stackoverflow.com/questions/7359702/…,
  • @JuanCarlosOropeza,我无法运行第一个查询来计算行数。运行需要 30 分钟以上。

标签: sql sql-server performance join


【解决方案1】:

我不确定结果是否相同。很可能不会。

考虑clientes 中与cdirightTable 中的行匹配但与cd_cliente 上的任何行都不匹配的行。第一个查询将返回匹配的一行。第二个查询将返回两行。一次用于匹配,一次用于不匹配,但由于left outer join,在rightTable 列中填充了空值。

此外,如果第一个查询返回任何合法的重复项,这些重复项将被第二个查询中的 union 运算符删除。

【讨论】:

  • 谢谢,请看版。如果我将其更改为内部连接,然后使用任何字段进行左连接,我会得到相同的结果吗? (关于重复,我不应该拥有它们)。
  • clientes.cd_cliente 是主键还是具有非空约束的唯一键?
【解决方案2】:

SQL Server 不适用于OR 和索引。不知道为什么。您的第二个查询是通过(很可能)两次通过索引搜索然后以某种方式合并它们来解决这个问题。

您可以尝试一些更简单的查询,例如这个:

SELECT
   a.cd_cliente,
   cdi_cli = a.cdi,
   b.* 
FROM
   dbo.clientes a 
   OUTER APPLY (
      SELECT *
      FROM dbo.rightTable b
      WHERE a.cdi = b.cdi
      UNION
      SELECT *
      FROM dbo.rightTable b
      WHERE a.cd_cliente = b.cdi
   ) b
;

这里有一个奇怪的方法,虽然我不确定:

SELECT
   a.cd_cliente,
   cdi_cli = a.cdi,
   b.*
FROM
   dbo.clientes a
   OUTER APPLY (
      SELECT *
      FROM dbo.rightTable b
      WHERE EXISTS (
         SELECT 1 WHERE a.cdi = c.cdi
         UNION
         SELECT 1 WHERE a.cd_cliente = b. cd_cliente
      )
   ) b
;

告诉过你这很奇怪!还有一个更奇怪的(可能是不可取的)。

SELECT
   a.cd_cliente,
   cdi_cli = a.cdi,
   BColumn1 = Max(BColumn1),
   BColumn2 = Max(BColumn2),
   BColumn3 = Max(BColumn3),
   BColumn4 = Max(BColumn4)
   -- all columns of B
FROM
   dbo.clientes a
   CROSS APPLY (VALUES
      (a.cdi),
      (a.cd_cliente)
   ) c (cdi)
   LEFT JOIN dbo.rightTable b
      ON c.cdi = b.cdi
GROUP BY
   a.cd_cliente,
   a.cdi,
   -- all columns of A
;

如果有一些时间来处理您的数据和索引并制定执行计划,我相信我们可以想出一些真正令人兴奋的东西。

【讨论】:

    【解决方案3】:

    这是您的原始查询:

    select a.cd_cliente, a.cdi as cdi_cli,b.* 
    from clientes a left join 
         rightTable b
         on a.cdi = b.cdi or a.cd_cliente = b.cdi;
    

    性能问题是由于on 条件中的or。这通常会干扰使用索引。

    如果您只关心b 的一列,您可以这样做:

    select a.cd_cliente, a.cdi as cdi_cli, coalesce(b1.col, b2.col) 
    from clientes a left join 
         rightTable b1
         on a.cdi = b1 left join
         rightTable b2
         on a.cd_cliente = b2.cdi;
    

    这些很容易概括为一小部分列,但如果b 很宽,则很麻烦。

    另一种编写查询的方式会更麻烦。它将从b 表开始,双左连接到a,然后从a 中合并剩余值:

    select coalesce(a1.cd_cliente, a2.cd_cliente) as cd_cliente,
           coalesce(a1.cdi, a2.cd) as cdi_cli,
           b.*
    from rightTable b left join
         clientes a1
         on a1.cdi = b.cdi left join
         clientes a2
         on a2.cd_cliente = b.cdi
    where a1.cdi is not null or c2.cdi is not null
    union all
    select a.cd_cliente, a.cdi, b.*
    from clientes a left join
         righttable b
         on 1 = 0
    where not exists (select 1 from righttable b where a.cdi = b.cdi) and
          not exists (select 1 from righttable b where a.cd_cliente = b.cdi)
    

    查询的第一部分将所有匹配行获取到一个或其他表。第二个添加不匹配的行。请注意 left join 的奇怪用法,其条件始终评估为 FALSE。这样可以更轻松地从b 引入表格。

    虽然这看起来很复杂,但连接和not exists 子查询都可以利用表上的适当索引。这意味着它应该有更合理的性能。

    【讨论】:

      猜你喜欢
      • 2013-02-26
      • 1970-01-01
      • 2012-01-03
      • 1970-01-01
      • 1970-01-01
      • 2011-01-11
      • 1970-01-01
      • 1970-01-01
      • 2014-01-12
      相关资源
      最近更新 更多