在最近的一次编程采访中,我被问到一个SQL问题,我给出了我认为合理的答案,但我的回答引起了dba的强烈反对,我无法弄清楚原因。
从那以后,我再一次考虑了这个问题,而且我无法弄清楚我的回答是多么可怕,所以我在这里寻求启示,找出正确的方法,或者不这样做,更好的方法从包含图书馆表和书籍表的数据库中生成图书馆报告及其中的图书数量。
我应该注意到我已经改变了一下场景,所以措辞与面试问题不一样,但任务是一样的。
以下是问题的最小架构:
create table library (
id integer primary key,
name char(8)
);
create table book (
id integer primary key,
name char(8),
library_id integer,
foreign key (library_id) references library(id)
);
任务是列出图书馆的名称及其中包含两本或更多书籍的图书的书籍数量。
而且,这是我提出的解决方案:
select
a.name as name,
b.nbooks as nbooks
from
library as a,
(
select
min(library_id) as library,
count(id) as nbooks
from
book
group by
library_id
) as b
where
( nbooks > 1 ) and (a.id = b.library)
;
第二个想法,使用明确的inner join
可能会更好。除此之外,您能否指出我潜在的陷阱(一般情况下或与特定数据库有关)以及生成此报告的正确方法?
答案 0 :(得分:4)
这是一种简单的方法:
select l.name, count(*) as numbooks
from library l join
books b
on l.id = b.library_id
group by l.name
having count(*) > 1
你的答案在技术上是可以接受的。 DBA可能并不关心其他人可能会遇到的某些风格事物(例如使用“a”作为库的别名而不是“l”)。子查询是不必要的,而min(library_id)
则不必要。您可以按列将聚合函数应用于组,但通常不会这样做。
DBA可能响应的最大问题是在WHERE
子句而不是ON
子句中具有连接条件。这很危险,因为如果你把它遗漏或者看起来像是一个无辜的修改,那么查询就可以变成一个CROSS JOIN而不是一个INNER JOIN。
答案 1 :(得分:2)
我至少看到一些严重的问题:1)不使用ANSI JOIN
语法,2)按library_id
分组,并在其上使用聚合函数。
我会这样写它来证明我知道如何在必要时返回其他库列时执行查询:
select l.lid, l.name, b.Count
from library l
inner join (
select library_id, count(*) as Count
from books
group by library_id
having Count > 1
) b on l.lid = b.library_id
我还要指出,如果两个库具有相同的名称,我特意没有按库名分组。
答案 2 :(得分:0)
DBA不喜欢的可能是子SELECT。在可能的情况下应该避免这些,因为它们通常具有非常差的性能(它们在代码形式中看起来也很丑陋)。
在这种情况下,最好使用JOIN。
SELECT library.name AS library
count( book.id ) AS books
FROM library
JOIN book ON book.library_id = library.id
GROUP BY book.id
HAVING count( book.id ) > 1