左连接表达式和Oracle返回的行数

时间:2014-11-21 18:29:13

标签: sql oracle join relational-database left-join

使用左连接的查询不返回记录,尽管左表中的where子句应该找到单个记录。在这种情况下,它应该返回一个记录,其中左表中的字段包含值,右表中的字段为null,因为它们之间没有匹配。

显然,使用引用连接表达式上的右表的大小写存在问题。

在SQL Server中,相同的查询按预期工作。

select 
  t1.Description, t2.Description
from 
  A t1 
left join 
  B t2 
on
  t1.Id = t2.Id and
  1 = case when (
    t2.Id = t2.Id and 
    (select t3.Flag from C t3 where t3.ID_B = t2.Id) = 'S'
  ) then 1 else 0 
  end
where t1.Id = 1

结果:没有返回任何行。

然后我移动了表达式t2.Id = t2.Id(这里只是为了演示问题,并且应该总是返回true,显然)来自case表达式。

select 
  t1.Description, t2.Description
from 
  A t1 
left join 
  B t2 
on
  t1.Id = t2.Id and
  t2.Id = t2.Id and 
  1 = case when ( 
    (select t3.Flag from C t3 where t3.ID_B = t2.Id) = 'S') then 1 else 0 
  end
where t1.Id = 1

结果:返回了一行。

上述查询仅用于演示问题,在实际情况下无用且未优化。

我想知道是否有人知道与此案相关的Oracle的任何限制。到目前为止,我们认为这是一个错误。

使用的数据:

  • A:Id = 1,描述=项目A1;
  • B:Id = 1,描述=项目B1;
  • C:Id = 1,Id_B = 2,Flag = S.

3 个答案:

答案 0 :(得分:1)

你认为t2.id = t2.id总是正确的假设是错误的。如果值为NULL,则将其视为false。我不认为这与这个特定的例子有关,只是为了澄清。

问题是如何处理left join。这个想法很简单。处理on子句。如果没有匹配项,则保留第一个表中的行。这与on子句中的内容无关。 (这是一个功能描述;有许多可能的实现。)

根据您的示例数据,Oracle不正确。应返回一行。 SQL Server示例还应返回一行。我怀疑数据可能略有不同;我个人在SQL Server(或Oracle)中从未遇到过left join的问题。

答案 1 :(得分:1)

CREATE TABLE t1 AS (SELECT 1 ID FROM dual);
CREATE TABLE t2 AS (SELECT 2 ID FROM dual);
CREATE TABLE t3 AS (SELECT 2 id_b, 's' flag FROM dual);

SELECT t1.* 
FROM t1 LEFT JOIN t2 
      ON t1.ID = t2.ID
         AND 1 = CASE WHEN t2.id = t2.id and (SELECT flag FROM t3 WHERE t3.id_b = t2.ID) = 's' THEN 1 ELSE 0 END
where t1.id = 1;

输出:no rows selected

结果看起来很奇怪,我想这可能是一个错误。

Oracle文档仅说明了 https://docs.oracle.com/cd/B28359_01/server.111/b28286/queries006.htm#SQLRF52337

  

您无法在任何WHERE子句中将列与子查询进行比较   外部联接,无论您指定哪种形式。

通过查看上述查询的计划,我可以看到这种情况:

 AND 1 = CASE WHEN t2.id = t2.id and (SELECT flag FROM t3 WHERE t3.id_b = t2.ID) = 's' THEN 1 ELSE 0 END

被解释为:

 CASE WHEN (T2.ID(+)=T2.ID(+) AND (SELECT FLAG FROM T3 T3 WHERE T3.ID_B=:B1)='s') THEN 1 ELSE 0 END =1

并且在加入后计算

我认为Oracle在执行连接之前无法计算CASE(因为T2.ID(+)=T2.ID(+)

答案 2 :(得分:1)

使用SQLFiddle Oracle 11g R2(感谢Shannon Severance)你的第一个查询给出记录计数:0但是只需删除CASE我们得到记录计数:1。(注意重命名t2Description。)

create table A (ID number(38), Description varchar(10));
create table B (ID number(38), Description varchar(10));
create table C (ID number(38), ID_B number(38), Flag varchar(10));

insert into A values(1, 'Item A1');
insert into B values(2, 'Item B1');
insert into C values(1, 2, 'S');

select 
  t1.Description, t2.Description as t2d
from 
  A t1 
left join 
  B t2 
on
  t1.Id = t2.Id and
    t2.Id = t2.Id and 
    (select t3.Flag from C t3 where t3.ID_B = t2.Id) = 'S'
where t1.Id = 1

这表明它与CASE错误估算有关。

请注意,在ON t2.Id中,至少有时(正确地)将其作为来自FROM产品的值,而不是在ON之后的NULL:

select 
  t1.Description, t2.Description as t2d
from 
  A t1 
left join 
  B t2 
on
    -- for above data t2.id should be 1 here
    t2.id is null
where t1.Id = 1
-- for above data t2.id should be null here

DESCRIPTION     T2D
Item A1     (null)

我找到了这个链接:Outer Join Bug in Oracle 12c?