2015-04-22 20 views
7

Ho le seguenti due relazioni:Riscrittura di istruzioni WITH in istruzioni di subquery in SQL?

Game(id, name, year) 
Devs(pid, gid, role) 

Dove Game.id è una chiave primaria, e dove Devs.gid è una chiave esterna per Game.id.

In un previous post I made here, un altro utente è stato così gentile da aiutarmi a creare una query che trova tutti i giochi realizzati con la maggior parte degli sviluppatori che hanno realizzato quel gioco. La sua risposta ha usato una dichiarazione WITH, e io non ho molta familiarità con questi, dato che sono solo poche settimane per imparare SQL. Ecco la domanda di lavoro:

WITH GamesDevs (GameName, DevsCount) 
AS 
(
    SELECT Game.name AS GameName, count(DISTINCT Devs.pid) AS DevsCount 
    FROM Game, Devs 
    WHERE Devs.gid=Game.id 
    GROUP BY Devs.gid, Game.name 
) 

SELECT * FROM GamesDevs WHERE GamesDevs.DevsCount = (SELECT MAX(DevsCount) FROM GamesDevs) 

Per il solo scopo di ottenere più familiarità con SQL, sto cercando di riscrivere la query utilizzando una sottoquery invece di un'istruzione with. Sto usando this Oracle documentation per aiutarmi a capirlo. Ho provato a riscrivere la query in questo modo:

SELECT * 
FROM (SELECT Game.name AS GameName, count(DISTINCT Devs.pid) AS DevsCount 
    FROM Game, Devs 
    WHERE Devs.gid=Game.id 
    GROUP BY Devs.gid, Game.name) GamesDevs 
WHERE GamesDevs.DevsCount = (SELECT MAX(DevsCount) FROM GamesDevs) 

Per quanto posso dire, queste due query dovrebbero essere identiche. Tuttavia, quando provo a eseguire la seconda query, viene visualizzato l'errore

Msg 207, livello 16, stato 1, riga 6 Nome colonna non valido 'DevsCount'.

Qualcuno sa perché potrei ricevere questo errore o perché queste due query non sarebbero identiche?

+1

Quale DBMS stai utilizzando? –

+0

Sto lavorando su Microsoft Azure Cloud! – Kyle

+0

Dovrai duplicare la sottoquery in ultimo dalla clausola –

risposta

4

Sarà necessario duplicare il subquery in ultimo dalla clausola LIKE:

SELECT * 
FROM (SELECT Game.name AS GameName, count(DISTINCT Devs.pid) AS DevsCount 
    FROM Game, Devs 
    WHERE Devs.gid=Game.id 
    GROUP BY Devs.gid, Game.name) GamesDevs 
WHERE GamesDevs.DevsCount = (SELECT MAX(DevsCount) FROM (SELECT Game.name AS GameName, count(DISTINCT Devs.pid) AS DevsCount 
    FROM Game 
    INNER JOIN Devs ON Devs.gid=Game.id 
    GROUP BY Devs.gid, Game.name)) 

Ma meglio farlo come:

SELECT TOP 1 WITH TIES Game.name AS GameName, count(DISTINCT Devs.pid) AS DevsCount 
FROM Game 
INNER JOIN Devs ON Devs.gid=Game.id 
GROUP BY Devs.gid, Game.name 
ORDER BY DevsCount DESC 
+0

@Turophile - sei sicuro? Per quanto ne so, 'GROUP' viene elaborato logicamente prima di' SELECT', quindi non ci dovrebbero essere dipendenze in quella direzione. –

+0

Grazie! Ho trovato che, nell'ultima riga, avevo bisogno di cambiarlo in "GROUP BY DEVS.gid, GAME.name) GamesDevs" per far sì che funzionasse. Perché la subquery deve essere duplicata? – Kyle

+0

@Damien_The_Unbeliever Ho pensato che fosse così, ma ho imparato SQL molti anni fa e da allora ci sono state molte modifiche alla sintassi e nuove varianti di SQL. Inoltre, potrei semplicemente sbagliare. Cancellerò il mio commento per evitare di confondere qualcuno. – Turophile

1

Il problema è questa linea:

WHERE GamesDevs.DevsCount = (SELECT MAX(DevsCount) FROM GamesDevs) 

Da un CTE si seleziona due volte. Ma non puoi da una query secondaria. La prima affermazione

WHERE GamesDevs.DevsCount 

è corretta. Ma

(SELECT MAX(DevsCount) FROM GamesDevs) 

non è corretto perché è possibile riutilizzare la sottoquery. Selezionando dal CTE, che agisce come una vista funziona è per questo che è possibile utilizzare sia un max e confrontare il conteggio

0

è possibile utilizzare RANK funzione finestra nel subquery al fine di individuare i record con il maggior numero di sviluppatori distinti per partita:

In questo modo non è necessario ripetere la query SELECT MAX(DevsCount). Devi solo selezionare quei record con rnk = 1.

Si consiglia inoltre di utilizzare ANSI-standard SQL per specificare il tipo esatto di operazione JOIN in uso.

@ La risposta di Giorgi che propone di utilizzare SELECT TOP 1 WITH TIES è una soluzione molto più pulita al problema originale.Ho appena fatto questo post come un modo alternativo per correggere la sottoquery che stai usando.