使用两个别名和两个比较将 *= 正确转换为 LEFT OUTER JOIN

Converting *= to LEFT OUTER JOIN correctly with two aliases and two comparisons

我想将 *= 的 SQL 语法转换为 LEFT OUTER JOIN。这是原始语法:

SELECT 
    a.col1,
    a.col2,
    b.col1,
    b.col2,
    b.col3,
    b.col4
FROM
    tableA AS a,
    tableB AS b
WHERE 
    a.col1 *= b.col1 AND
    a.col2 *= b.col2 AND
    b.col3 = "xxx"
    b.col4 = "yyy"

我正在尝试以下操作,但由于加入,returns 行太多了。我做错了什么?

SELECT 
    a.col1,
    a.col2,
    b.col1,
    b.col2,
    b.col3,
    b.col4
FROM
    tableA AS a
LEFT OUTER JOIN tableB AS b ON a.col1 = b.col1
LEFT OUTER JOIN tableB ON a.col2 = b.col2   
WHERE 
    b.col3 = "xxx"
    b.col4 = "yyy"

您可以在 JOIN 语法中使用 AND

SELECT 
    a.col1,
    a.col2,
    b.col1,
    b.col2,
    b.col3,
    b.col4
FROM
    tableA AS a

LEFT OUTER JOIN tableB AS b ON a.col1 = b.col1 AND a.col2 = b.col2   
WHERE 
    b.col3 = "xxx"
    b.col4 = "yyy"

一个LEFT JOIN就够了。你能试试这个吗

SELECT 
    a.col1,
    a.col2,
    b.col1,
    b.col2,
    b.col3,
    b.col4
FROM
    tableA AS a
    LEFT OUTER JOIN tableB AS b ON a.col1 = b.col1 AND a.col2 = b.col2 
WHERE 
    b.col3 = "xxx"
    AND b.col4 = "yyy"

试试下面的示例代码

SELECT a.col1, a.col2, b.col1, b.col2, b.col3, b.col4
FROM tableA AS a
LEFT OUTER JOIN tableB AS b ON a.col1 = b.col1 AND a.col2 = b.col2
WHERE 
b.col3 = "xxx" AND b.col4 = "yyy"

您的第一个查询仅涉及 一个 两个 table 的左连接,因为涉及同一对别名的所有 *= 比较都用于一个离开加入。所以我们把它们聚集成一个 left joinon.

当只剩下一对table加入from时,非*的一列table与常量的比较也是左连接标准的一部分。所以它也进入 on:

In a left join, the outer table and inner table are the left and right tables respectively.

If you submit a query with an outer join and a qualification on a column from the inner table of the outer join, the results may not be what you expect. The qualification in the query does not restrict the number of rows returned, but rather affects which rows contain the null value. For rows that do not meet the qualification, a null value appears in the inner table’s columns of those rows.

这给出:

SELECT 
    a.col1, a.col2, b.col1, b.col2, b.col3, b.col4
FROM
    tableA AS a
LEFT OUTER JOIN tableB AS b
ON a.col1 = b.col1 AND a.col2 = b.col2
AND b.col3 = "xxx" ??? b.col4 = "yyy"

有关完整的语义,请参阅 Transact-SQL® Users Guide for Adaptive Server® Enterprise 15.7。特别是 * 一些多连接查询的左连接语义从版本 12.0 开始发生了变化。

备注:

  • 正如 philipxy 所提到的,提供 minimal, complete and verifiable example 将对解决您的问题有很大帮助;特别是我们不知道您的源数据是什么样的,您当前收到的结果,也不知道您想要生成的结果(所以像 "gives exactly the same results" 这样的评论对我们来说毫无意义,因为我们没有示例通过)
  • 正如其他人所提到的,您只需要 left (outer) join 到 TableB 的一份副本(使用 2/4 on 子句 - 更多内容见下文)
  • 您没有提到您使用的是哪个 Sybase RDBMS 产品,所以我假设您使用的是 Sybase ASE

出于讨论目的,我将创建几个表和一些示例数据:

use tempdb
go

if object_id('tableA') is not NULL drop table tableA
go

if object_id('tableB') is not NULL drop table tableB
go

create table tableA
(col1   int
,col2   int
)
go

insert tableA values (1,1)
insert tableA values (1,2)
insert tableA values (2,3)
insert tableA values (2,4)
go

create table tableB
(col1   int
,col2   int
,col3   varchar(10)     null
,col4   varchar(10)     null
)
go

insert tableB values (1,1,'xxx','yyy')
insert tableB values (1,2,null,null)
go

使用旧的外部联接编写方式(即 *==*),所有 where 子句都在联接操作期间应用,从而得到以下结果:

select  a.col1,
        a.col2,
        b.col1,
        b.col2,
        b.col3,
        b.col4

from    tableA AS a,
        tableB AS b

where   a.col1 *= b.col1
and     a.col2 *= b.col2
and     b.col3  = "xxx"
and     b.col4  = "yyy"
go

 col1        col2        col1        col2        col3       col4
 ----------- ----------- ----------- ----------- ---------- ----------
           1           1           1           1 xxx        yyy
           1           2        NULL        NULL NULL       NULL
           2           3        NULL        NULL NULL       NULL
           2           4        NULL        NULL NULL       NULL

虽然第一条 tableA 记录基于 a) 2 个搜索参数 (SARG) 子句 (col3=xxx, col4=yyy) 和 b) 连接子句找到匹配...其他 3 tableA 条记录未找到匹配项(主要是因为 2 个 SARG 子句没有匹配项)因此 所有 tableB 列都填充了 NULL。

我们可以使用以下 left (outer) join 样式查询生成相同的结果集:

select  a.col1,
        a.col2,
        b.col1,
        b.col2,
        b.col3,
        b.col4

from    tableA as a
left
join    tableB as b

on      a.col1 = b.col1
and     a.col2 = b.col2
and     b.col3 = "xxx"
and     b.col4 = "yyy"
go

 col1        col2        col1        col2        col3       col4
 ----------- ----------- ----------- ----------- ---------- ----------
           1           1           1           1 xxx        yyy
           1           2        NULL        NULL NULL       NULL
           2           3        NULL        NULL NULL       NULL
           2           4        NULL        NULL NULL       NULL

同样,SARG 子句作为连接操作的一部分应用。

现在,如果 objective 在 连接操作完成后应用 SARG 子句 ,我们需要将 SARG 移动到一个单独的 where 条款;要了解这是如何工作的,我们首先 运行 仅包含连接子句的查询(即删除 SARG 子句):

select  a.col1,
        a.col2,
        b.col1,
        b.col2,
        b.col3,
        b.col4

from    tableA as a
left
join    tableB as b

on      a.col1 = b.col1
and     a.col2 = b.col2
go

 col1        col2        col1        col2        col3       col4
 ----------- ----------- ----------- ----------- ---------- ----------
           1           1           1           1 xxx        yyy
           1           2           1           2 NULL       NULL
           2           3        NULL        NULL NULL       NULL
           2           4        NULL        NULL NULL       NULL

通过将 SARG 子句作为单独的 where 子句应用,我们现在可以仅过滤掉我们想要的行(即 col3=xxx 和 col4=yyy),如下所示:

select  a.col1,
        a.col2,
        b.col1,
        b.col2,
        b.col3,
        b.col4

from    tableA as a
left
join    tableB as b

on      a.col1 = b.col1
and     a.col2 = b.col2

-- apply the SARGs **after** the join:

where   b.col3 = "xxx"
and     b.col4 = "yyy"
go

 col1        col2        col1        col2        col3       col4
 ----------- ----------- ----------- ----------- ---------- ----------
           1           1           1           1 xxx        yyy

因此,在这一点上我们可以看到可以从 left (outer) join 查询样式生成几个不同的结果集...哪个结果是 'correct' 将取决于最终结果您尝试生成的结果...返回到关于提供 minimal, complete and verifiable example

的评论