为啥选择单个属性返回的行数少于选择 Oracle SQL 中的所有列

Posted

技术标签:

【中文标题】为啥选择单个属性返回的行数少于选择 Oracle SQL 中的所有列【英文标题】:Why selecting a single attribute returns less rows than selecting all columns in oracle SQL为什么选择单个属性返回的行数少于选择 Oracle SQL 中的所有列 【发布时间】:2021-02-25 11:32:33 【问题描述】:

创建的表和进行的查询不是这个问题的主要焦点,让我困惑的是为什么第一个查询和第二个查询返回不同的行数

drop table Reserves;
drop table Sailors;
drop table Boats;

create table Sailors (
    sid      char(1) not null,
    sname    char(1) not null,
    rating   int,
    age      int not null,
    primary key (sid)
);

create table Boats (
    bid      char(1) not null,
    bname    char(1) not null,
    color    varchar(5),
    primary key (bid)
);

create table Reserves (
    sid      char(1) not null,
    bid      char(1) not null,
    rdate    int not null,
    primary key (sid, bid, rdate),
    foreign key (sid) references Sailors(sid)
        on delete cascade,
    foreign key (bid) references Boats(bid)
        on delete cascade
);


------------------------------------------------------------------------------

-- Insert values

insert into Sailors values ('1', 'q', 90, 24);
insert into Sailors values ('0', 's', 60, 22);
insert into Sailors values ('2', 'd', 80, 20);
insert into Sailors values ('3', 'w', 70, 18);
insert into Sailors values ('4', 'a', 60, 19);
insert into Sailors values ('5', 'l', 80, 17);
insert into Sailors values ('6', 'o', 90, 18);
insert into Sailors values ('7', 'q', 70, 20);
insert into Sailors values ('8', 'd', 60, 16);
insert into Sailors values ('9', 'i', 80, 22);

insert into Boats values ('0', 'U', 'red');
insert into Boats values ('1', 'P', 'red');
insert into Boats values ('2', 'Q', 'blue');
insert into Boats values ('3', 'C', 'green');
insert into Boats values ('4', 'L', 'blue');
insert into Boats values ('5', 'O', 'blue');
insert into Boats values ('6', 'A', 'red');
insert into Boats values ('7', 'C', 'red');
insert into Boats values ('8', 'Y', 'green');
insert into Boats values ('9', 'N', 'blue');

insert into Reserves values ('0', '0', 3);
insert into Reserves values ('0', '1', 2);
insert into Reserves values ('0', '2', 1);
insert into Reserves values ('0', '2', 3);
insert into Reserves values ('1', '0', 4);
insert into Reserves values ('3', '2', 2);
insert into Reserves values ('4', '0', 3);
insert into Reserves values ('4', '0', 1);
insert into Reserves values ('4', '1', 3);
insert into Reserves values ('4', '6', 4);
insert into Reserves values ('4', '7', 1);
insert into Reserves values ('5', '8', 2);
insert into Reserves values ('5', '9', 2);
insert into Reserves values ('7', '4', 4);
insert into Reserves values ('7', '5', 1);
insert into Reserves values ('8', '3', 2);
insert into Reserves values ('9', '3', 3);
insert into Reserves values ('9', '0', 1);
insert into Reserves values ('9', '6', 1);
insert into Reserves values ('9', '8', 2);

commit;


select *
from Sailors join Boats on color='red' natural left outer join Reserves
where rdate is null;

select sid
from Sailors join Boats on color='red' natural left outer join Reserves
where rdate is null;

我想找到没有订购所有红船的水手的 sid,上面的第一个查询返回了我期望的正确行,但是第二个查询只返回 sid=2 和 sid=6 的行,尽管这两个查询是相同的。 sid 2 和 6 的水手是唯一没有预订任何船的水手。

【问题讨论】:

我认为第一个查询是返回您想要的记录,而不是其他任何事情——因为编写的 SQL 是毫无意义的。当您说“没有订购所有红船的水手”时,您是指没有预订红船的水手还是没有预订红船的水手? 侧面观察:在 SAILORS 中有一个 AGE 列。但“年龄”总是在变化。与其尝试存储某物/某人不断变化的“年龄”,不如将起源/出生日期存储为 DATE。然后你总是可以通过简单地从 sysdate 中减去birth_date 来准确计算年龄。 @NickW - 我还没有检查查询;我假设你是对的。但即便如此,问题仍然存在。这两个查询如果不正确,即使输出错误,也应该在输出中产生相同数量的行。 OP 询问为什么不是这样。 @mathguy Thx,这正是我要问的问题,创建的表和上面编写的查询绝对不实用,但我更好奇为什么第二个查询只返回带有 sid= 的行2 和 sid=6,而第一个查询返回所有具有空 rdate 列的行 @NickW,因为有多个红船,我的目标是找到根本没有预订的水手,或者有预订但没有预订所有红船的水手。 【参考方案1】:

据我所知,这看起来像是 Oracle 的 natural [...] join 实现中的一个错误。我会做一些测试,看看它是否也会影响内部连接。

可以使用left|right|inner join USING(...) 语法代替natural 连接,并在using 子句中给出列名列表。列列表应该是连接的两个成员中具有相同名称的 ALL 列的列表。

对您提供的数据进行了非常简单的实验(即使仅针对该数据也 +1)表明结果与我们在第一个查询中写入 using(sid, bid) 相同,但在第二个查询中仅写入 using(sid)。如果在任一查询中 - 无论选择什么,无论是 * 还是 sid - 您使用 using 语法,您在输出中获得的行数与您的第一个或第二个查询相同,具体取决于 你在using 子句中输入了什么。

所以,Oracle 对第二个查询所做的完全是错误的。我只能推测,但我相信 Oracle 会首先查看 SELECT 子句,也许还会查看其他子句,看看它需要从每个表中检索哪些。 (例如,这告诉优化器它可以使用哪些索引,等等。)在这一步,Oracle 决定——在你的第二个查询中——它不需要bid。然后,当它将“自然”连接转换为自己的内部代码时,它不会将bid 作为连接列输入。这是错误的 - 这就是我称之为“错误”的原因。

重要提示:其他人评论说这两个查询都是“错误的”,因为它们不能解决您试图解决的问题。这可能完全正确。我什至没有看你的问题说明;在这里,我正在回答您的问题,无论问题如何,这都是有效的-也就是说,为什么两个查询会产生不同的行数。即使它们对您的用例来说是“错误的”,它们本质上也应该给出“相同”的错误答案,而不是“不同”的错误答案。这是我上面讨论的唯一内容。

【讨论】:

【参考方案2】:

说实话,查询是... errr... 不正确

例如,它为您提供 sid = 0 和 sid = 1 的水手。这些水手买了红色的船

这是您需要显示没有购买红船的水手的 SQL。

select sl.* 
  from Sailors sl
 where not exists (select 1
                     from Reserves rs
                     join Boats bs on rs.bid = bs.bid
                    where bs.color = 'red' and rs.sid = sl.sid);

【讨论】:

【参考方案3】:

这 2 个查询将为您提供您在上一条评论中所要求的内容。我已经将它们联合起来为您提供了一个 SID 列表,但显然如果您想要 22 个单独的列表,您可以独立运行它们。

-- Sailors with no reservations
select s.sid
from sailors s
where s.sid not in (
    select sid from reserves)
union
-- Sailors with reservations but not of red boats
select s.sid
from sailors s
where s.sid not in (
Select distinct r.sid
from reserves r
inner join boats b on r.bid = b.bid
where b.color = 'red');

【讨论】:

以上是关于为啥选择单个属性返回的行数少于选择 Oracle SQL 中的所有列的主要内容,如果未能解决你的问题,请参考以下文章

查询在 JDBC 中使用时返回的行数少于 SQL 开发人员

dplyr transmute 返回的行数少于原始数据帧

Oracle SQL Select 中的行数?

当我已经使用游标时,为啥我得到“精确提取返回的行数超过请求的行数”?

《系统架构师》——数据库系统

通过均匀跳过行来选择固定的行数