web-dev-qa-db-fra.com

Comment sélectionner les N premières lignes de chaque groupe?

J'ai deux tables SQLite comme celle-ci:

 AuthorId | AuthorName
----------------------
 1        | Alice
 2        | Bob
 3        | Carol
 ...      | ....


 BookId | AuthorId | Title
----------------------------------
 1      | 1        | aaa1
 2      | 1        | aaa2
 3      | 1        | aaa3
 4      | 2        | ddd1
 5      | 2        | ddd2
 ...    | ...      | ...
 19     | 3        | fff1
 20     | 3        | fff2
 21     | 3        | fff3
 22     | 3        | fff4

Je veux faire une requête SELECT qui retournera les N (par exemple deux) lignes pour chaque AuthorId, en les classant par titre ("Sélectionnez les deux premiers livres de chaque auteur").

Exemple de sortie:

 BookId |  AuthorId | AuthorName | Title
------------------------------------------
 1      |  1        |   Alice    | aaa1
 2      |  1        |   Alice    | aaa1
 4      |  2        |   Bob      | ddd1
 5      |  2        |   Bob      | ddd2
 19     |  3        |   Carol    | fff1
 20     |  3        |   Carol    | fff2

Comment puis-je créer cette requête?

(Oui, j'ai trouvé un sujet similaire et je sais comment renvoyer une seule ligne (première ou première). Le problème est avec les deux).

23
Vladislav

Vous pouvez effectuer le comptage à l'aide d'une sous-requête corrélée:

SELECT b.BookId, a.AuthorId, a.AuthorName, b.Title
FROM Author a join
     Book b
     on a.AuthorId = b.AuthorId
where (select count(*)
       from book b2
       where b2.bookId <= b.BookId and b2.AuthorId = b.AuthorId
      ) <= 2;

Pour une petite base de données, cela devrait convenir. Si vous créez un index composite sur Book(AuthorId, BookId), cela aidera la requête.

17
Gordon Linoff

Il existe une variante alternative:

SELECT * FROM (
    SELECT * FROM BOOK, AUTHOR
    WHERE BOOK.AUTHORID = AUTHOR.AUTHORID
) T1
WHERE T1.BOOKID IN (
    SELECT T2.BOOKID FROM BOOK T2
    WHERE T2.AUTHORID = T1.AUTHORID
    ORDER BY T2.BOOKTITLE
    LIMIT 2
)
ORDER BY T1.BOOKTITLE
14
fasked