SQL - Query - max (count())

Я изучаю свой экзамен по базам данных (завтра), и у меня возникают проблемы с упражнениями, когда меня просят написать запросы. Вот пример:

enter image description here

Меня просят написать запрос, чтобы ответить на следующий вопрос: Среди авторов с самым низким возрастом, у кого написано большинство книг?

Проблема мой учитель запрещает мне использовать подзапросы внутри предложения 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

(заметьте, что Шон не написал ни одной книги, книга №6 имеет 2 автора, а Рик - автор большинства книг (4))

Теперь код, который я написал выше, дает этот результат (я думаю):

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

(Самый низкий возраст - 25, а Лена и Иоанн - 25)

Спрашивается:

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

(Лена - автор, среди всех авторов с самым низким возрастом (25), причем большинство книг написано)

Заранее спасибо

Ответы

Ответ 1

Поскольку вы студент, я отвечу на часть вопроса. Вот ответ, игнорирующий самую младшую часть:

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
                      )

Я думаю, вы можете понять, как его изменить.

Кстати, ограничение на limit и top, надеюсь, только для этого примера. В противном случае вы должны получить другого учителя, так как это очень важные конструкции.

Кроме того, вам нужно изучить обычный синтаксис соединения, а не , в предложении from. Еще раз, если вы учитель не преподаете современный sytnax (с 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 явно, два join неявно (условие возраста) и два group by s. Первый будет иметь лучшую производительность, чем последняя.

С точки зрения читаемости эта версия короче и чище. Я также думаю, что гораздо легче научить тому, что это делает, а не "необычные" конструкции в первой версии. Большинство учеников поймут, что делают top и order 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 похоже на выполнение гонки с тремя ногами. Да, вы, вероятно, можете добраться туда, но вы будете там быстрее работать на своих двух ногах.

Ответ 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: Признаюсь, я узнал о ALL из Гордона Линоффа.

Ответ 3

Если вам нужен только один результат, выберите верхний, а порядок должен сделать все остальное. Я лично сделал бы функцию ранжирования для явного получения ранжирования с использованием оконной функции 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

Ответ 4

Я понимаю, что вам просто нужна 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

Ответ 5

Если вам разрешено использовать CTE и RANK, это будет триумф.

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 Fiddle

Демо, где Джон пишет другую книгу