2017-07-01 1 views
0

J'ai un examen final dans les bases de données et comme je résolvais quelques exemples de questions, j'ai rencontré quelques problèmes.SQL - INTERSECT avec HAVING + MAX d'un SUM

J'ai une relation de plusieurs à plusieurs entre 2 tables.

Player     PlayerTournament    Tournament 
-------     --------------------   ------------------- 
pk id_player    fk id_player     pk id_tournament 
name      fk id_tournament    name 
rank      year       city 
country     victories      court_surface 
                 tournament_type 

Ce que je dois faire est:

1). Listez les joueurs (nom et pays) qui, en 2016, ont gagné au moins un match dans un tournoi d'argile, mais n'ont participé à aucun tournoi d'herbe.

2). Dressez la liste des joueurs (nom, pays, nombre total de victoires) avec le plus grand nombre de victoires.

Je pensais à quelque chose comme ceci:

1. SELECT P.NAME, P.COUNTRY 
FROM Player P INNER JOIN PlayerTournament PT 
ON P.ID_PLAYER= PT.ID_PLAYER 
INNER JOIN Tournament T 
ON T.ID_TOURNAMENT= PT.ID_TOURNAMENT 
WHERE T.COURT_SURFACE="clay" 
GROUP BY (something) 
HAVING SUM(PT.VICTORIES)>=1 
INTERSECT 
(same select and inner joins) 
WHERE T.COURT_SURFACE="grass" 
GROUP BY (something) 
HAVING COUNT(ID_PLAYER)=0 

2.SELECT P.NAME, P.COUNTRY, SUM(PT.VICTORIES) 
FROM Player P INNER JOIN PlayerTournament PT 
ON P.ID_PLAYER= PT.ID_PLAYER 
GROUP BY ... 
HAVING sum of victories = max sum of victories 

Je ne sais pas si la façon dont je pense que le problème est correct, je besoin d'aide pour les déclarations « ayant ».

Répondre

0

Q1: INTERSECT ne fonctionnera pas, car COUNT(ID_PLAYER) ne sera jamais NULL. Vous devez passer à EXCEPT sans HAVING.

Une autre solution standard pour ce type de requête (même Sélectionnez avec des conditions différentes) utilise agrégation conditionnelle où les deux conditions sont déplacés dans différents cas:

SELECT P.NAME, P.COUNTRY 
FROM Player P INNER JOIN PlayerTournament PT 
ON P.ID_PLAYER= PT.ID_PLAYER 
INNER JOIN Tournament T 
ON T.ID_TOURNAMENT= PT.ID_TOURNAMENT 
WHERE T.COURT_SURFACE IN ('clay', 'grass') 
GROUP BY (something) 
HAVING SUM(CASE WHEN T.COURT_SURFACE = 'clay' THEN PT.VICTORIES END)>=1 
AND COUNT(CASE WHEN T.COURT_SURFACE = 'grass' THEN PT.ID_PLAYER END)=0 

Q2: Cela semble demander le meilleur joueur par pays, mais votre avoir ne peut pas être écrit directement comme agrégats imbriqués rares non autorisés. Vous pouvez utiliser un sous-requête à la place Corrélées:

WITH cte AS 
(
    SELECT P.NAME, P.COUNTRY, SUM(PT.VICTORIES) AS sumVic 
    FROM Player P INNER JOIN PlayerTournament PT 
    ON P.ID_PLAYER= PT.ID_PLAYER 
    GROUP BY ... 
) 
SELECT * 
FROM cte AS t1 
WHERE sumVic = 
(SELECT MAX(sumVic) 
    FROM cte AS t2 
    WHERE t1.ID_PLAYER = t2.ID_PLAYER 
) 

En utilisant plus modernes fenêtré les fonctions d'agrégation vous pouvez faire le calcul MAX dans le même niveau:

WITH cte AS 
(
    SELECT P.NAME, P.COUNTRY, SUM(PT.VICTORIES) AS sumVic, 
     MAX(SUM(PT.VICTORIES)) OVER (PARTITION BY P.COUNTRY) AS maxVic 
    FROM Player P INNER JOIN PlayerTournament PT 
    ON P.ID_PLAYER= PT.ID_PLAYER 
    GROUP BY ... 
) 
SELECT * 
FROM cte AS t1 
WHERE sumVic = maxVic