3

在最近的一次编程面试中,我被问到一个 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可能会更好。除此之外,您能否向我指出潜在的陷阱(一般的或与特定数据库相关的)以及生成此报告的正确方法?

4

3 回答 3

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。

于 2012-10-02T14:31:31.320 回答
2

我至少看到了一些严重的问题:1)不使用 ANSIJOIN语法,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 

我还要指出,我特别没有按库名称分组,以防两个库具有相同的名称。

于 2012-10-02T14:35:44.127 回答
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
于 2012-10-02T14:38:00.117 回答