我需要某种有条件的加入

时间:2010-09-24 18:40:41

标签: sql sql-server sql-server-2008 left-join

好的,我知道有一些帖子讨论过这个问题,但我的问题无法通过关于连接的条件where语句来解决(通用解决方案)。

我有三个join语句,根据查询参数,我可能需要运行三者的任意组合。我的Join语句非常昂贵,所以我只想在查询需要时才进行连接,而且我不准备编写7组合IF..ELSE..语句来实现这些组合。

这是迄今为止我用于解决方案的内容,但所有这些都不太理想:

LEFT JOIN joinedTable jt
ON jt.someCol = someCol
WHERE jt.someCol = conditions
OR @neededJoin is null

(这太贵了,因为即使我不需要它,我也在执行连接,只是不评估连接)

OUTER APPLY
(SELECT TOP(1) * FROM joinedTable jt 
WHERE jt.someCol = someCol
AND @neededjoin is null)

(这比总是离开加入更贵)

SELECT @sql = @sql + ' INNER JOIN joinedTable jt ' +
             ' ON jt.someCol = someCol ' +
             ' WHERE (conditions...) '

(这个是IDEAL,现在是如何写的,但我试图将它从动态SQL中转换出来)。

任何想法或帮助都会很棒!

修改 如果我采用动态SQL方法,我试图找出在构造查询方面最有效的方法。鉴于我有三个可选条件,我需要所有这些条件的结果,我当前的查询是这样的:

IF condition one
SELECT from db
INNER JOIN condition one

UNION

IF condition two
SELECT from db
INNER JOIN condition two

UNION

IF condition three
SELECT from db
INNER JOIN condition three

我的非动态查询通过执行左连接执行此任务:

SELECT from db
LEFT JOIN condition one
LEFT JOIN condition two
LEFT JOIN condition three
WHERE condition one is true
OR condition two is true
OR condition three is true

哪个更有意义呢?既然“SELECT from db”语句中的所有代码都是一样的?似乎联合条件更有效,但我的查询非常长,因为它....

谢谢!

6 个答案:

答案 0 :(得分:2)

LEFT JOIN
joinedTable jt ON jt.someCol = someCol AND jt.someCol = conditions AND @neededjoin ...
...

OR

LEFT JOIN
(
SELECT col1, someCol, col2 FROM joinedTable WHERE someCol = conditions AND @neededjoin ...
) jt ON jt.someCol = someCol
...

OR

;WITH jtCTE AS
(SELECT col1, someCol, col2 FROM joinedTable WHERE someCol = conditions AND @neededjoin ...)
SELECT
...
LEFT JOIN
jtCTE ON jtCTE.someCol = someCol
...

老实说,除非你使用文字,否则没有像条件JOIN那样的构造。

如果它在SQL语句中被评估...所以不要在SQL语句中使用动态SQL或IF ELSE

答案 1 :(得分:1)

动态sql解决方案通常是针对这些情况的最佳解决方案,但是如果你真的需要摆脱这种情况,那么stroed porc中的一系列if语句就可以完成这项工作。这是一个痛苦,你必须编写更多的代码,但它会比在语句本身中尝试连接条件更快。

答案 2 :(得分:1)

我会采用这样简单直接的方法:

DECLARE @ret TABLE(...) ;

IF <coondition one> BEGIN ;
  INSERT INTO @ret() SELECT ...
END ;

IF <coondition two> BEGIN ;
  INSERT INTO @ret() SELECT ...
END ;

IF <coondition three> BEGIN ;
  INSERT INTO @ret() SELECT ...
END ;

SELECT DISTINCT ... FROM @ret ;

编辑:我建议使用表变量,而不是临时表,这样程序就不会在每次运行时重新编译。一般来说,三个更简单的插入更有可能获得更好的执行计划,而不是一个巨大的怪物查询结合这三个。

然而,我们无法猜测性能。我们必须通过基准来确定它。然而,更简单的代码块更易于提高可读性和可维护性。

答案 3 :(得分:1)

试试这个:

LEFT JOIN joinedTable jt
   ON jt.someCol = someCol
   AND jt.someCol = conditions
   AND @neededJoin = 1 -- or whatever indicates join is needed

我认为你会发现它表现良好,能满足你的需求。

<强>更新

如果这没有给出我声称的性能,那么也许那是因为我上次使用连接到表时这样做了。我需要的值可以来自3个表中的一个,基于2列,所以我建立了一个'join-map'表,如下所示:

Col1  Col2 TableCode
  1     2    A
  1     4    A
  1     3    B
  1     5    B
  2     2    C
  2     5    C
  1     11   C

然后,

SELECT
   V.*,
   LookedUpValue =
      CASE M.TableCode
      WHEN 'A' THEN A.Value
      WHEN 'B' THEN B.Value
      WHEN 'C' THEN C.Value
      END
FROM
    ValueMaster V
    INNER JOIN JoinMap M ON V.Col1 = M.oOl1 AND V.Col2 = M.Col2
    LEFT JOIN TableA A ON M.TableCode = 'A'
    LEFT JOIN TableB B ON M.TableCode = 'B'
    LEFT JOIN TableC C ON M.TableCode = 'C'

这使我在查询这些表格时获得了巨大的性能提升(其中大部分都是数十或数亿行表)。

这就是为什么我问你是否真的获得了改进的性能。当然,它会在执行计划中加入一个联接,并为其分配一些成本,但总的来说,将比一些只是不加选择地加入所有3个表然后{{1}的计划做更少的工作找到正确的价值。

如果你发现与动态SQL相比,以这种方式进行连接的成本只有5%,但是随着不加选择的连接成本高出100%,由于正确性,你可能不值得这样做,清晰度和动态SQL的简洁性,所有这些都可能比一个小的改进更有价值(当然,取决于你正在做什么)。

成本与行数的关系是否也是另一个需要考虑的因素。如果即使拥有大量数据,您只能在每秒运行数十次的查询中节省200ms的CPU,使用它也是明智之举。

我一直在坚持认为它会表现良好的原因是,即使使用哈希匹配,也不会有任何要探测的行,或者它不会有任何行来创建哈希的。与使用初始帖子的WHERE子句OR样式查询相比,哈希操作会更早停止。

答案 4 :(得分:0)

动态SQL解决方案在大多数方面都是最好的;您尝试使用不同数量的连接运行不同的查询,而不重写查询以执行不同数量的连接 - 这在性能方面效果不佳。


当我在大约前一段时间(比如早期的90年代)做这种事情时,我使用的语言是I4GL,查询是使用其CONSTRUCT语句构建的。这用于生成WHERE子句的一部分,因此(基于用户输入),它生成的过滤条件可能如下所示:

a.column1 BETWEEN 1 AND 50 AND
b.column2 = 'ABCD' AND
c.column3 > 10

在那些日子里,我们没有现代的JOIN符号;我们要去的时候要做一点即兴表演。通常,核心表(或一组核心表)始终是查询的一部分;还有一些表可以选择性地作为查询的一部分。在上面的例子中,我假设'c'是主表的别名。代码的工作方式是:

  • 请注意,查询中引用了表'a':
    • 将“FullTableName AS a”添加到FROM子句
    • 将连接条件'AND a.join1 = c.join1'添加到WHERE子句
  • 请注意表'b'被引用...
    • 向FROM子句和WHERE子句添加位。
  • 从select-list(通常是固定的),FROM子句和WHERE子句组装SELECT语句(偶尔也会使用GROUP BY,HAVING或ORDER BY等装饰)。

这里应该使用相同的基本技术 - 但细节略有不同。

首先,你没有要分析的字符串;您可以从其他情况了解需要添加到查询中的表。所以,你仍然需要设计一些东西,以便它们可以组装,但是......

  • SELECT子句及其select-list可能已修复。它将标识查询中必须存在的表,因为值是从这些表中提取的。
  • FROM子句可能包含一系列连接。

    • 一部分将是核心查询:

      FROM CoreTable1 AS C1
      JOIN CoreTable2 AS C2
           ON C1.JoinColumn = C2.JoinColumn
      JOIN CoreTable3 AS M
           ON M.PrimaryKey = C1.ForeignKey
      
    • 可以根据需要添加其他表格:

      JOIN AuxilliaryTable1 AS A
           ON M.ForeignKey1 = A.PrimaryKey
      
    • 或者您可以指定完整查询:

      JOIN (SELECT RelevantColumn1, RelevantColumn2
              FROM AuxilliaryTable1
             WHERE Column1 BETWEEN 1 AND 50) AS A
      
    • 在第一种情况下,您必须记住将WHERE标准添加到主WHERE子句,并信任DBMS Optimizer将条件移动到JOIN表中,如图所示。一个好的优化器会自动完成;一个穷人可能不会。使用查询计划来帮助您确定DBMS的功能。

  • 为连接操作中未涵盖的任何表间条件添加WHERE子句,并根据核心表添加任何筛选条件。请注意,我主要考虑额外的标准(AND操作)而不是替代标准(OR操作),但只要你小心地对表达式进行充分括号化,你就可以处理OR。

  • 有时,您可能需要添加几个JOIN条件才能将表连接到查询的核心 - 这并不是非常不寻常。

  • 添加任何GROUP BY,HAVING或ORDER BY子句(或限制或任何其他装饰)。

请注意,您需要充分了解数据库架构和连接条件。基本上,这是用编程语言编写的,就像你必须考虑构建查询一样。只要你理解了这个和你的架构,就没有任何不可克服的问题。

祝你好运......

答案 5 :(得分:0)

仅仅因为没有其他人提到这一点,这里有你可以使用的东西(不是动态的)。如果语法看起来很奇怪,那是因为我在Oracle中测试过它。

基本上,您将已连接的表转换为具有where子句的子选择,如果条件不匹配,则子句不返回任何内容。如果条件匹配,则子选择返回该表的数据。 Case语句允许您选择整体选择中返回的列。

with m as (select 1 Num, 'One' Txt from dual union select 2, 'Two' from dual union select 3, 'Three' from dual),
t1 as (select 1 Num from dual union select 11 from dual),
t2 as (select 2 Num from dual union select 22 from dual),
t3 as (select 3 Num from dual union select 33 from dual)
SELECT m.*
      ,CASE 1
         WHEN 1 THEN
          t1.Num
         WHEN 2 THEN
          t2.Num
         WHEN 3 THEN
          t3.Num
       END SelectedNum
  FROM m
  LEFT JOIN (SELECT * FROM t1 WHERE 1 = 1) t1 ON m.Num = t1.Num
  LEFT JOIN (SELECT * FROM t2 WHERE 1 = 2) t2 ON m.Num = t2.Num
  LEFT JOIN (SELECT * FROM t3 WHERE 1 = 3) t3 ON m.Num = t3.Num