为什么在 oracle 中选择单个属性 returns 行比选择所有列少 SQL
Why selecting a single attribute returns less rows than selecting all columns in oracle SQL
创建的表和进行的查询不是这个问题的主要关注点,让我困惑的是为什么第一次查询和第二次查询returns行数不同
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,上面的第一个查询 returns 是我期望的正确行,尽管第二个查询 returns 只有行使用 sid=2 和 sid=6,尽管这两个查询是相同的。 sid 为 2 和 6 的水手是唯一没有预订任何船的水手。
老实说,查询是...错误...不正确
例如,它为您提供了 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);
据我所知,这看起来像是 Oracle 的 natural [...] join
实现中的一个 bug。我会做一些测试,看看它是否也会影响内部联接。
代替 natural
连接,可以使用语法 left|right|inner join USING(...)
并在 using
子句中给出列名列表。列列表应该是在联接的两个成员中具有相同名称的 ALL 列的列表。
对您提供的数据进行的非常简单的实验(即使仅此数据也是 +1)表明结果与我们在第一个查询中写入 using(sid, bid)
相同,但只有 using(sid)
在第二。如果在任一查询中 - 无论选择什么,无论是 *
还是 sid
- 你使用 using
语法,你在输出中得到的行数与你的第一个或你的第二个查询,取决于 你在 using
子句中输入的内容。
所以,Oracle 对第二个查询所做的完全是错误的。我只能推测,但我相信 Oracle 首先会查看 SELECT
子句,也许还会查看其他子句,以了解 columns它需要从每个 table 中检索。 (例如,这会告诉优化器它可以使用哪些索引等。)在这一步,Oracle 决定 - 在您的第二个查询中 - 它不需要 bid
。然后,当它将“自然”连接翻译成它自己的内部代码时,它不会将 bid
作为连接列。这是错误的 - 这就是为什么我称其为“错误”。
重要说明:其他人评论说这两个查询都是“错误的”,因为它们没有解决您试图解决的问题。这可能是完全正确的。我什至没有看你的问题说明;我在这里回答你的问题,无论问题如何,这都是有效的——也就是说,为什么这两个查询产生不同数量的行。即使它们对于您的用例是“错误的”,它们本质上也应该给出“相同”的错误答案,而不是“不同”的错误答案。这是我上面唯一讨论的事情。
这 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');
创建的表和进行的查询不是这个问题的主要关注点,让我困惑的是为什么第一次查询和第二次查询returns行数不同
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,上面的第一个查询 returns 是我期望的正确行,尽管第二个查询 returns 只有行使用 sid=2 和 sid=6,尽管这两个查询是相同的。 sid 为 2 和 6 的水手是唯一没有预订任何船的水手。
老实说,查询是...错误...不正确
例如,它为您提供了 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);
据我所知,这看起来像是 Oracle 的 natural [...] join
实现中的一个 bug。我会做一些测试,看看它是否也会影响内部联接。
代替 natural
连接,可以使用语法 left|right|inner join USING(...)
并在 using
子句中给出列名列表。列列表应该是在联接的两个成员中具有相同名称的 ALL 列的列表。
对您提供的数据进行的非常简单的实验(即使仅此数据也是 +1)表明结果与我们在第一个查询中写入 using(sid, bid)
相同,但只有 using(sid)
在第二。如果在任一查询中 - 无论选择什么,无论是 *
还是 sid
- 你使用 using
语法,你在输出中得到的行数与你的第一个或你的第二个查询,取决于 你在 using
子句中输入的内容。
所以,Oracle 对第二个查询所做的完全是错误的。我只能推测,但我相信 Oracle 首先会查看 SELECT
子句,也许还会查看其他子句,以了解 columns它需要从每个 table 中检索。 (例如,这会告诉优化器它可以使用哪些索引等。)在这一步,Oracle 决定 - 在您的第二个查询中 - 它不需要 bid
。然后,当它将“自然”连接翻译成它自己的内部代码时,它不会将 bid
作为连接列。这是错误的 - 这就是为什么我称其为“错误”。
重要说明:其他人评论说这两个查询都是“错误的”,因为它们没有解决您试图解决的问题。这可能是完全正确的。我什至没有看你的问题说明;我在这里回答你的问题,无论问题如何,这都是有效的——也就是说,为什么这两个查询产生不同数量的行。即使它们对于您的用例是“错误的”,它们本质上也应该给出“相同”的错误答案,而不是“不同”的错误答案。这是我上面唯一讨论的事情。
这 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');