SQL:何时以及为什么允许两个 on 条件?

标签 sql sql-server tsql postgresql join

问题:

我最近遇到了一个有趣的 SQL 问题。
我必须获得租赁对象的租赁契约(Contract)。

问题是,每个房间可能有多个租赁契约(Contract),每个房间可能有多个租赁对象。

但是,由于糟糕的数据库修补,租赁契约(Contract)分配给了房间,而不是租赁对象。因此,我必须获取契约(Contract)编号,并将其与租赁对象编号进行比较,以获得正确的结果。

我认为这样可以:

SELECT * 
FROM T_Room 

LEFT JOIN T_MAP_Room_LeasingObject 
    ON MAP_RMLOBJ_RM_UID = T_Room.RM_UID 

LEFT JON T_LeasingObject
    ON LOBJ_UID = MAP_RMLOBJ_LOBJ_UID

LEFT JOIN T_MAP_Room_LeasingContract  
    ON T_MAP_Room_LeasingContract.MAP_RMCTR_RM_UID = T_Room.RM_UID 

LEFT JOIN T_Contracts 
    ON T_Contracts.CTR_UID = T_MAP_Room_LeasingContract.MAP_RMCTR_CTR_UID 
    AND T_Contracts.CTR_No LIKE ( ISNULL(T_LeasingObject.LOBJ_No, '') + '.%'     ) 

WHERE ...

但是,因为在我有契约(Contract)号之前就加入了映射表,没有映射表我就无法获得契约(Contract)号,所以我有双倍的条目。

问题有点复杂,因为没有租赁契约(Contract)的房间也需要出现,所以我不能只使用内部连接。

经过一些试验,我发现这可以按预期工作:

SELECT * 
FROM T_Room 

LEFT JOIN T_MAP_Room_LeasingObject 
    ON MAP_RMLOBJ_RM_UID = T_Room.RM_UID 

LEFT JON T_LeasingObject
    ON LOBJ_UID = MAP_RMLOBJ_LOBJ_UID

LEFT JOIN T_MAP_Room_LeasingContract  

LEFT JOIN T_Contracts 
    ON T_Contracts.CTR_UID = T_MAP_Room_LeasingContract.MAP_RMCTR_CTR_UID 
    ON T_MAP_Room_LeasingContract.MAP_RMCTR_RM_UID = T_Room.RM_UID 
    AND T_Contracts.CTR_No LIKE ( ISNULL(T_LeasingObject.LOBJ_No, '') + '.%'     ) 

WHERE ...

我现在明白为什么一个连接中的两个 on 条件(通常由查询设计者提供)很有用,以及它有什么不同。

我想知道这是 MS-SQL/T-SQL 特定的东西,还是标准的 sql。

所以我尝试在 PostgreSQL 中使用另外 3 个表。

所以我在另外 3 个表上写了这个查询:

SELECT * 
FROM t_dms_navigation

LEFT JOIN t_dms_document 
    ON NAV_DOC_UID = DOC_UID 

 LEFT JOIN t_dms_project 
    ON PJ_UID = NAV_PJ_UID 

并试图把它变成一个有条件的两个

SELECT * 
FROM t_dms_navigation

LEFT JOIN t_dms_document 

 LEFT JOIN t_dms_project 
    ON PJ_UID = NAV_PJ_UID 
    ON NAV_DOC_UID = DOC_UID 

所以我认为它是特定于 t-sql 的,但很快也在 MS-SQL 中进行了尝试,令我惊讶的是发现它在那里也不起作用。

我认为这可能是因为缺少外键,所以我在我的房间查询中删除了所有表上的外键,但它仍然不起作用。

所以我的问题是:
为什么 2 on conditions 甚至是合法的,它有名称吗?为什么它在我的第二个示例中不起作用?

最佳答案

这是标准的 SQL。每个 JOIN 都必须有一个相应的 ON 子句。您所做的只是改变连接在 1 中发生的顺序 - 这有点像更改表达式的括号以绕过优先规则。

A JOIN B ON <cond1> JOIN C ON <cond2>

首先根据cond1加入AB。然后它采用该组合行集并根据 cond2 将其连接到 C

A JOIN B JOIN C ON <cond1> ON <cond2>

首先在cond1的基础上加入BC。然后它根据 cond2 获取 A 并将其连接到之前的组合行集。


它应该在 PostgreSQL 中工作 - 这是 documentation 的相关部分SELECT 语句的:

where from_item can be one of:
[ ONLY ] table_name [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
( select ) [ AS ] alias [ ( column_alias [, ...] ) ]
with_query_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
function_name ( [ argument [, ...] ] ) [ AS ] alias [ ( column_alias [, ...] | column_definition [, ...] ) ]
function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] )
from_item [ NATURAL ] join_type from_item [ ON join_condition | USING ( join_column [, ...] ) ]

相关的是最后一行。请注意,这是一个递归定义 - 连接左侧和右侧可以是任何东西 - 包括更多连接。


1与 SQL 一样,这是逻辑处理顺序 - 系统可以按照它认为最有效的任何顺序自由执行物理处理,只要结果是一致的。

关于SQL:何时以及为什么允许两个 on 条件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18976284/

相关文章:

mysql - 使用逗号分隔的用户输入搜索逗号分隔的数据库字段

SQL更新与连接

sql-server - 如何在 ETL 管道中正确截断临时表?

SQL查询在内部联接期间连接两列

tsql - 在 SQL Server 2012 中的多级层次结构查询中按一定顺序排列子项

MySQL LEFT JOIN 日期与顺序

Mysql查询不使用索引

sql - 将同一表中的一列的值更新到 SQL Server 中的另一列

sql-server - 如何在数据流任务SSIS 2008 r2中传递变量

sql - 根据计数有条件地将记录插入多线程环境中的表中