请帮助将 Tsql“隐式连接”转换为显式连接

Posted

技术标签:

【中文标题】请帮助将 Tsql“隐式连接”转换为显式连接【英文标题】:Please help transform Tsql "implicit joins" into explicit ones 【发布时间】:2011-03-23 10:49:52 【问题描述】:

对不起,我几乎是一个 SQL 菜鸟。这必须在 MSFT SQL、Oracle 和 Sybase 中工作。在下面的 sn-p 中,我需要在 IJ.PO_id = KL.PO_id 上将 IJKL 之间的 inner join 更改为 left join,也在 IJ.PO_id = KL.PO_id 上。所以,我相信我必须重新考虑这一点。好吧,隐式连接并不是最易读的,至少在我的同事看来。我想我会同意,直到我发展出自己的品味。抱歉,为了以防万一,我修改了表名和字段名。

/* @IJ_id is an input stored proc patrameter. */
from AB, 
     CD,
     EF,
     GH,
     IJ,
     KL
where
    EF.EF_id = IJ.EF_id and
    IJ.EF_id = AB.EF_id and
    EF.ZY_id = IJ.ZY_id and
    IJ.ZY_id = AB.ZY_id and
    IJ.IJ_id = AB.IJ_id and
    IJ.IJ_id = @IJ_id and
    EF.XW_id = GH.GH_id and
    AB.VU_code = CD.VU_code and
    IJ.TS > 0 and
    IJ.RQ = 0 and
    EF.RQ = 0 and
    AB.RQ = 0 and
    IJ.PO_id = KL.PO_id;

现在,我的困难在于 where 子句中发生了很多事情。看起来不像 a.b = c.d 的东西将保留在 where 子句中,但并非所有看起来像 a.b = c.d 的东西看起来都很容易转换为显式连接。困难的部分是理想情况下,邻居之间的条件是-AB+CDCD+EFEF+GHGH+IJIJ+KL,但他们现在还没有那么有条理。我可以重新订购一些,但最终我不想忘记我的目标:我希望新查询不会变慢,并且我希望新查询的可读性不会降低。似乎我最好只修改我需要更改的部分,并使其基本保持不变。我不确定我是否可以这样做。

如果您理解我的意图,请提出更好的查询。如果你没有,那么请告诉我如何改进这个问题。谢谢。

【问题讨论】:

按照目前的写法,这些都将是 INNER JOIN,而您正在重写为显式 INNER JOIN,因此您可以更改为 LEFT JOIN? 你是对的。我只想将其中一个更改为左连接,IJ+KL: IJ.PO_id = KL.PO_id。所以,我认为将所有其他的重写为显式内部连接会 A) 使我的更改成为可能,B) 使其更具可读性。 很高兴看到您正在重写它,因为 t-sql 在任何使用隐式连接的版本中都无法正确处理左连接。 这是为什么隐式连接更难维护的完美例子! @HLGEM,我想毫无头绪和不知道那会是什么样子是有好处的:) 【参考方案1】:

我觉得应该是这样的:

FROM AB
JOIN CD ON AB.VU_code = CD.VU_code
JOIN IJ ON IJ.EF_id = AB.EF_id AND IJ.ZY_id = AB.ZY_id AND IJ.IJ_id = AB.IJ_id
JOIN EF ON EF.EF_id = IJ.EF_id AND EF.ZY_id = IJ.ZY_id
JOIN GH ON EF.XW_id = GH.GH_id
JOIN KL ON IJ.PO_id = KL.PO_id
WHERE
    IJ.IJ_id = @IJ_id AND
    IJ.TS > 0 AND
    IJ.RQ = 0 AND
    EF.RQ = 0 AND
    AB.RQ = 0

我已尝试按照以下规则排列表格:

每个连接条件都会提到它在一侧连接的新表。 如果该表尚未连接,则连接条件中不会提及该表。 其中一个操作数为常量的条件保留为 WHERE 条件。

最后一条规则很困难 - 无法从您错位的名称中判断一个条件是否应该是连接的一部分或 where 子句的一部分。对于 INNER JOIN,两者都会给出相同的结果。条件是连接的一部分还是 where 子句的一部分取决于表之间关系的语义。

您需要根据具体情况考虑每个条件:

是否定义了两个表之间的关系?将其放入 JOIN 中。 它是对结果的过滤器吗?放在 WHERE 子句中。

一些准则:

包含来自用户的参数的条件不太可能是应该移至联接的条件。 在连接条件中通常不会发现不等式。

【讨论】:

从其他示例中我可以看到,确实某些条件可以是连接的一部分或 where 子句的一部分。我天真的理解是,如果我帮助查询优化器尽早消除尽可能多的事情,我会帮助它完成它的工作。此查询是“严重”存储过程的一部分,该过程在较大的表(从几百到几千条记录)上运行。在这种情况下,如果我必须在可读性和性能之间做出选择,那么我会选择性能。 75% 的用户将在 MSFT SQL 上运行,20% 的时间在 oracle 上,5% 的时间在 Sybase 上(如果有帮助的话) @Hamish Grubijan:选择 WHERE 还是 ON 不是性能问题——性能应该是一样的。当您将其更改为 LEFT JOIN 时,您将获得不同的结果,具体取决于您是否在连接内或作为 where 子句的一部分具有常量条件。我不认为我可以给你一个永远有效的规则。您需要根据具体情况确定哪些去向。 @Hamish Grubijan:使用 ANSI-92 JOIN 语法时,在 WHERE 与 JOIN 中设置标准会真正影响返回的结果。连接中的条件(IE:IJ.RQ = 0)在连接表之前运行。 马克,所以作为练习,我开始填写 Excel 表格以帮助我确保翻译是否符合犹太教规,但这很快让我发明了一种符号。我的下一个想法是编写一个 Python 脚本,这让我想到 - 以前一定有人这样做过。所以,我的问题是 - 您是否知道用于数据库的重构工具。我们支持 Ora、Syb 和 MSFT SQL,但我们是一家 MSFT 商店,通过批量订阅获得他们的大部分软件。我们也会考虑其他供应商,因为 MSFT 并不总是最好的。我想要一些类似 re-sharper 的 SQL 代码。 其实我刚找到***.com/questions/2192655/…【参考方案2】:

它不可能比你给出的例子更不可读......

from AB a
join CD c on a.VU_Code = c.VU_Code
join EF e on a.EF_id = e.EF_id and e.RQ = 0
join GH g on e.XW_id = g.GH_id
join IJ i on a.IJ_id = i.IJ_id and e.EF_id = i.EF_id
         and a.EF_id = i.EF_id and e.ZY_id = i.ZY_id
         and a.ZY_id = i.ZY_id and i.TS > 0 and i.RQ = 0
LEFT join KL k on i.PO_id = k.PO_id
where 
    i.IJ_id = @IJ_id and 
    a.RQ = 0

【讨论】:

'它的可读性不可能比你给出的例子更差......' 你把这些话从我嘴里说出来了。 哈哈,你差点写了一些可读性差的东西。虽然不太好,但还是试试吧:P【参考方案3】:

用途:

 FROM AB t1
 JOIN CD t2 ON t2.VU_code = t1.VU_code
 JOIN GH t4 ON t4.gh_id = t3.xw_id
 JOIN IJ t5 ON t5.ZY_id = t1.ZY_id
           AND t5.IJ_id = t1.IJ_id
           AND t5.EF_id = t1.EF_id 
           AND t5.IJ_id = @IJ_id 
           AND t5.TS > 0 
           AND t5.RQ = 0 
 JOIN EF t3 ON t3.ef_id = t5.ef_id
           AND t3.zy_id = t5.zy_id
           AND t3.RQ = 0
 JOIN KL t6 ON t6.po_id = t5.po_id -- Add LEFT before JOIN for LEFT JOIN
WHERE ab.qu = 0

它们按照原始 ANSI-89 语法的顺序进行了别名,但由于别名引用而调整了顺序 - 在定义之前无法引用表别名。

这是 ANSI-92 JOIN 语法 - 没有性能优势,但这确实意味着 OUTER 连接语法是一致的。只需在“JOIN KL ...”之前添加 LEFT 即可将其转换为 LEFT JOIN。

【讨论】:

顺便说一句,其中之一应该是左连接。 t1、t2 等是别名吗?如果是这样,我不确定是否要在生产代码中使用它们。再次抱歉,我宁愿不透露真实姓名。只是好奇 - 您是否使用 t1 ... t6 来帮助您实现这一目标? @Hamish Grubijan:我正在更新,因为您正在评论解决 LEFT JOIN。见更新。表别名由您选择 - 随意使用。

以上是关于请帮助将 Tsql“隐式连接”转换为显式连接的主要内容,如果未能解决你的问题,请参考以下文章

浏览器不为显式域保存 cookie

为啥带有 unsigned long long 参数的 std::bitset 构造函数未标记为显式?

隐式转换和显式转换及强制转换的区别

来自OPENJONS()的TSQL,具有显式架构:十进制(38,30)数据类型的空字符串

如何在 ASP.NET 响应中将 Transfer-Encoding 设置为显式或隐式分块?

显式命令有效,而其 TSQL 等效项引发错误