程序上将子查询转换为连接

Jus*_* R. 16 sql algorithm join subquery relational-model

是否存在将SQL子查询转换为连接的通用过程或算法,反之亦然?也就是说,是否有一组排版操作可以应用于语法正确的SQL查询语句,其中包含一个子查询,该子查询导致函数等效语句没有子查询?如果是这样,它们是什么(即算法是什么),在什么情况下它们不适用?

OMG*_*ies 24

将子查询转换为JOIN可以非常简单:

IN 条款

 FROM TABLE_X x
WHERE x.col IN (SELECT y.col FROM TABLE_Y y)
Run Code Online (Sandbox Code Playgroud)

...可以转换为:

FROM TABLE_X x
JOIN TABLE_Y y ON y.col = x.col
Run Code Online (Sandbox Code Playgroud)

您的JOIN标准是您直接比较的地方.

EXISTS 条款

但是当你看这个EXISTS条款时会有一些复杂的问题.EXISTS 通常correllated,其中所述子查询是通过从子查询外部的表(多个)标准过滤.但EXISTS仅用于根据条件返回布尔值.

 FROM TABLE_X x
WHERE EXISTS (SELECT NULL
                FROM TABLE_Y y
               WHERE y.col = x.col)
Run Code Online (Sandbox Code Playgroud)

...转化:

FROM TABLE_X x
JOIN TABLE_Y y ON y.col = x.col
Run Code Online (Sandbox Code Playgroud)

由于布尔值,结果集中存在更多行出现的风险.

SELECT在SELECT子句中

这些应该总是偏见的情况下改变:

SELECT x.*,
       (SELECT MAX(y.example_col)
          FROM TABLE_Y y
         WHERE y.col = x.col)
  FROM TABLE_X x
Run Code Online (Sandbox Code Playgroud)

你现在可能注意到了一个模式,但是对于内联视图示例我做了一点点不同:

SELECT x.*,
       z.mc
  FROM TABLE_X x
  JOIN (SELECT y.col, --inline view within the brackets
               MAX(y.example_col) 'mc'
          FROM TABLE_Y y
      GROUP BY y.col) z ON z.col = x.col
Run Code Online (Sandbox Code Playgroud)

关键是确保内联视图结果集包括连接所需的列以及列.

LEFT JOIN小号

您可能已经注意到我没有任何LEFT JOIN示例 - 只有在子查询中的列使用NULL测试时才会这样做(COALESCE这几天几乎任何数据库,Oracle NVLNVL2MySQL IFNULL,SQL Server ISNULL等等):

SELECT x.*,
       COALESCE((SELECT MAX(y.example_col)
          FROM TABLE_Y y
         WHERE y.col = x.col), 0)
  FROM TABLE_X x
Run Code Online (Sandbox Code Playgroud)

转化:

   SELECT x.*,
          COALESCE(z.mc, 0)
     FROM TABLE_X x
LEFT JOIN (SELECT y.col,
                  MAX(y.example_col) 'mc'
             FROM TABLE_Y y
         GROUP BY y.col) z ON z.col = x.col
Run Code Online (Sandbox Code Playgroud)

结论

我不确定这是否能满足您的印刷需求,但希望我已经证明关键在于确定JOIN标准是什么.一旦知道所涉及的列,就会知道所涉及的表格.


Rob*_*ley 10

这个问题依赖于关系代数的基本知识.你需要问问自己正在进行什么样的连接.例如,LEFT ANTI SEMI JOIN就像一个WHERE NOT EXISTS子句.

某些连接不允许重复数据,有些不允许删除数据.其他人允许额外的字段可用.我在我的博客中讨论了这个问题,网址是http://msmvps.com/blogs/robfarley/archive/2008/11/09/join-simplification-in-sql-server.aspx

此外,请不要觉得你需要在JOIN中做所有事情.查询优化器应该为您处理所有这些,并且您经常可以更难以维护这种方式查询.您可能会发现自己使用了一个广泛的GROUP BY子句,并且有一个有趣的WHERE .. IS NULL过滤器,它只用于断开业务逻辑与查询设计的连接.

SELECT子句中的子查询(本质上是查找)仅提供额外的字段,而不是重复或消除.因此,您需要确保在JOIN中强制执行GROUP BY或DISTINCT值,并使用OUTER JOIN来保证行为相同.

WHERE子句中的子查询永远不能复制数据,或者为SELECT子句提供额外的列,因此您应该使用GROUP BY/DISTINCT来检查它.在哪里存在类似的东西.(这是LEFT SEMI JOIN)

什么不存在(LEFT ANTI SEMI JOIN)不提供数据,并且不重复行,但可以消除...为此你需要做LEFT JOIN并寻找NULL.

但是查询优化器应该为您处理所有这些.我实际上喜欢在SELECT子句中偶尔使用子查询,因为它非常清楚我没有复制或删除行.QO可以为我整理它,但是如果我使用视图或内联表值函数,我想向那些追随我的人说清楚QO可以简化它.查看原始查询的执行计划,您将看到系统正在为您提供INNER/OUTER/SEMI连接.

你真正需要避免的事情(至少在SQL Server中)是使用BEGIN和END的函数(例如标量函数).他们可能觉得他们简化了代码,但实际上它们将在一个单独的上下文中执行,因为系统将它们视为程序性的(不可简化).

我在最近的SQLBits V会议上做过关于此类事情的会议.这是录制的,所以你应该能够在某个时刻观看它(如果你能忍受我的笑话!)