15

我正在准备我的数据库系统考试(明天),但在要求我编写查询的练习中遇到了麻烦。这是一个例子:

在此处输入图像描述

我被要求写一个查询来回答以下问题:在年龄最小的作者中,谁写的书最多?

问题是我的老师禁止我在 FROM 子句中使用子查询,并使用 TOP

我已经写了一个答案,但我知道一个答案是不正确的:

SELECT W.AName, COUNT(W.ID_B) AS NBooks
FROM Write W, Author A1
WHERE (A1.AName = W.AName) AND
      (A1.AAge = (SELECT MIN(A2.Age)
                  FROM Author A2))
GROUP BY W.AName
ORDER BY NBooks DESC

这个给出了所有年龄较小的作者,以及他们各自写的书数量(我希望..)。正确答案应该只是这一行的第一行。

让我说清楚:

Table Author
AName    | AAge
---------------
John     | 25
Rick     | 30
Sean     | 26
Lena     | 25

Table Writes
AName    | ID_B
---------------
John     | 2
Lena     | 1
John     | 3
Lena     | 4
Rick     | 5
Rick     | 6
Lena     | 6
Rick     | 7
Rick     | 8

(注意 Sean 没有写任何书,第 6 本书有 2 个作者,而 Rick 是拥有最多书籍的作者 (4) )

现在,我上面写的代码给出了这个结果(我猜):

AName    | NBooks
-----------------
Lena     | 3
John     | 2

(最低年龄是 25 岁,莉娜和约翰都是 25 岁)

问的是:

AName    | NBooks
-----------------
Lena     | 3

(Lena 是作者,在所有年龄最小的作者中(25 岁),着书最多)

提前致谢

4

5 回答 5

5

因为你是学生,所以我会回答部分问题。这是一个答案,忽略了最年轻的部分:

select a.AName, COUNT(*) as NumBooks
from Author a join
     Write w
     on a.AName = w.AName
group by a.AName
having count(*) >= all(select COUNT(*) as NumBooks
                       from write w
                       group by w.AName
                      )

我想你可以弄清楚如何修改它。

顺便说一句,我希望对limitand的限制top仅适用于这个例子。否则,你应该找另一位老师,因为这些都是非常重要的结构。

此外,您需要学习常规的连接语法,而不是,from子句中。再一次,如果你的老师没有教现代语法(大约从 1988 年开始),那就换一个新老师。而且,我假设对子查询的限制也适用于 CTE。

我还想指出查询的“正确”版本:

select top 1 a.aname, count(*) as NumBooks
from Author a join
     Write w
     on a.AName = w.AName
group by author.name, author.Aage
order by author.Age asc, count(*) desc

这个查询几乎在任何维度上都比上面的查询好。它做一个join,一个group by和一个排序。我的查询的完整版本join显式执行两个 s,join隐式执行两个 s(age 子句),以及两个group bys。前者将比后者有更好的性能。

从可读性的角度来看,这个版本更短更简洁。我还认为教它在做什么比第一个版本中的“不寻常”构造要容易得多。大多数学生会理解toporder by正在做什么,并且可以效仿。模仿该条款中发生的事情having需要一些心理体操。

如果要获取最大计数的所有作者,首先要意识到前面的查询相当于:

select aname, NumBooks
from (select a.aname, count(*) as NumBooks,
             row_number() over (partition by author.Name order by a.aAge, count(*) desc) as seqnum
      from Author a join
           Write w
           on a.AName = w.AName
      group by author.name, author.Aage
     ) aw
where seqnum = 1

切换它以获取所有作者很容易:

select aname, NumBooks
from (select a.aname, count(*) as NumBooks,
             dense_rank() over (partition by author.Name order by a.aAge, count(*) desc) as seqnum
      from Author a join
           Write w
           on a.AName = w.AName
      group by author.name, author.Aage
     ) aw
where seqnum = 1

这也比回答问题的查询更有效。不能top在子句中使用或子查询from就像在进行三足赛跑。是的,你可能会到达那里,但你会用自己的两条腿跑得更快。

于 2013-01-11T21:23:26.443 回答
2

这是一些限制,但它可以利用它的创造力。

因此,您需要一位最年轻的作者,他所写的书籍数量高于(或等于)另一位最年轻作者所写的任何其他书籍数量……

SELECT
  [a1].[AName],
  [a1].[AAge],
  COUNT(*) AS [NBooks]
FROM [Author] [a1], [Writes] [w1]
WHERE 
  [a1].[AName] = [w1].[AName]
  AND [a1].[AAge] = (SELECT MIN([a2].[AAge]) FROM [Author] [a2])
GROUP BY 
  [a1].[AName],
  [a1].[AAge]
HAVING COUNT(*) >= ALL
  (SELECT
    COUNT(*) AS [NBooks]
  FROM [Author] [a3], [Writes] [w2]
  WHERE 
    [a3].[AName] = [w2].[AName]
    AND [a3].[AAge] = (SELECT MIN([a4].[AAge]) FROM [Author] [a4])
    AND [a3].[AName] <> [a1].[AName]
  GROUP BY 
    [a3].[AName],
    [a3].[AAge])

PS:不得不承认,我是ALLGordon Linoff那里了解到的。

于 2013-01-11T21:50:06.950 回答
1

如果您只想要一个结果,请选择前一个结果,其余的应该由排序完成。我个人会做一个排名函数来使用 Aggregate() Over() 窗口函数显式地获得排名。但是,由于您正在学习,他们可能还不想提出这个问题并向您展示“顶部”是如何工作的。

declare @Person Table ( personID int identity, person varchar(8), age int);

insert into @Person values ('Brett', 34),('John', 34),('Peter', 52);

declare @Books Table ( BookID int identity, personID int);

insert into @Books values (1),(1),(1),(2),(2),(3)

Select top 1 -- TOP WILL LIMIT TO CHOICE YOU WANT BASED ON ORDER BY CLAUSE
    p.person
,   p.age
,   count(b.BookID) as cnts
from @Person p, @Books b
where p.personID = b.personID
group by p.person, p.age
order by age, cnts desc
于 2013-01-11T20:40:22.170 回答
1

如果您被允许使用 CTE 并对其进行排名,则很简单。

WITH cte 
     AS (SELECT a.aname, 
                A.aage, 
                Count(id_b)                             Book_Count, 
                RANK() 
                  OVER( 
                    ORDER BY a.aage, Count(id_b) DESC ) rn 
         FROM   author a 
                INNER JOIN writes w 
                        ON a.aname = w.aname 
         GROUP  BY a.aname, 
                   a.aage) 
SELECT aname, 
       Book_Count
FROM   cte 
WHERE  rn = 1 

SQL小提琴

演示约翰写另一本书的地方

于 2013-01-11T21:03:26.540 回答
1

我知道您只想要 1 行结果;

您可以先限制作者,然后通过使用内部连接,您可以从 Write 表中检索他的姓名和书籍数量。

SELECT W.AName, COUNT(W.ID_B) AS NBooks
FROM Write W INNER JOIN Author A1 ON A1.AName = W.AName
WHERE 
A1.AName = (SELECT AName FROM Write GROUP BY AName ORDER BY COUNT(ID_B) DESC)
AND A1.AAge = (SELECT MIN(A2.Age) FROM Author A2)
GROUP BY W.AName
ORDER BY NBooks DESC
于 2013-01-11T20:59:45.273 回答