Perché SQL mi obbliga a ripetere tutti i campi non aggregati dalla mia clausola SELECT nella mia clausola GROUP BY? [chiuso]

StackOverflow https://stackoverflow.com/questions/416625

  •  03-07-2019
  •  | 
  •  

Domanda

Questo mi ha infastidito per molto tempo.

Il 99% delle volte, la clausola GROUP BY è una copia esatta della clausola SELECT, meno le funzioni aggregate (MAX, SUM, ecc.). Questo infrange il principio Don't Repeat Yourself.

Quando la clausola GROUP BY non può contenere una copia esatta della clausola SELECT meno le funzioni aggregate?

modifica

Mi rendo conto che alcune implementazioni ti consentono di avere campi diversi in GROUP BY rispetto a SELECT (quindi 99%, non 100%), ma sicuramente è un'eccezione molto minore?
Qualcuno può spiegare cosa dovrebbe essere restituito se si utilizzano campi diversi?

Grazie.

È stato utile?

Soluzione

Tendo ad essere d'accordo con te: questo è uno dei molti casi in cui SQL dovrebbe avere impostazioni predefinite leggermente più intelligenti per salvarci tutti un po 'di digitazione. Ad esempio, immagina se questo fosse legale:

Select ClientName, InvoiceAmount, Sum(PaymentAmount) Group By *

dove " * " significava "tutti i campi non aggregati". Se tutti sapessero che ha funzionato, non ci sarebbe confusione. Potresti sottotitolare un elenco specifico di campi se volessi fare qualcosa di complicato, ma lo splat significa "tutto". (che in questo contesto significa, tutti i possibili ).

Concesso, " * " significa qualcosa di diverso qui rispetto alla clausola SELECT, quindi forse un personaggio diverso funzionerebbe meglio:

Select ClientName, InvoiceAmount, Sum(PaymentAmount) Group By !

Ci sono alcune altre aree come quella in cui SQL non è così eloquente come potrebbe essere. Ma a questo punto, probabilmente è troppo radicato per apportare molti cambiamenti di questo genere.

Altri suggerimenti

Poiché sono due cose diverse, puoi raggruppare per elementi che non sono nella clausola select

EDIT:

Inoltre, è sicuro fare questo presupposto?

Ho un'istruzione SQL

Select ClientName, InvAmt, Sum(PayAmt) as PayTot

È " corretto " per il server supporre che voglio raggruppare per ClientName AND InvoiceAmount? Personalmente preferisco (e penso che sia più sicuro) avere questo codice

Select ClientName, InvAmt, Sum(PayAmt) as PayTot
Group By ClientName

genera un errore che mi richiede di cambiare il codice in

Select ClientName, Sum(InvAmt) as InvTot, Sum(PayAmt) as PayTot
Group By ClientName

Spero / mi aspetto che presto vedremo qualcosa di più completo; una lezione di storia SQL sull'argomento sarebbe utile e istruttiva. Chiunque? Chiunque? Bueller?

Nel frattempo, posso osservare quanto segue:

SQL precede il principio DRY, almeno per quanto è stato documentato in The Pragmatic Programmer .

Non tutti i DB richiedono l'elenco completo: Sybase, ad esempio, eseguirà felicemente query come

SELECT a, b, COUNT(*)
FROM some_table
GROUP BY a

... che (almeno ogni volta che ho accidentalmente lanciato un tale mostro) porta spesso a record di dati così enormi e involontari che ne conseguono rapidamente richieste di panico, implorando i DBA di far rimbalzare il server. Il risultato è una sorta di prodotto cartesiano parziale, ma penso che potrebbe essere principalmente un errore da parte di Sybase nell'implementare correttamente lo standard SQL.

Forse abbiamo bisogno di un modulo abbreviato: chiamalo GroupSelect

GroupSelect Field1, Field2, sum(Field3) From SomeTable Where (X = "3")

In questo modo, il parser deve solo lanciare un errore se si lascia fuori una funzione aggregata.

La buona ragione è che si otterrebbero risultati errati il ??più delle volte se non si specificassero tutte le colonne. Supponiamo di avere tre colonne, col1 , col2 e col3 .

Supponi che i tuoi dati siano così:

Col1  Col2 Col3
a      b    1
a      c    1
b      b    2
a      b    3

seleziona col1, col2, sum (col3) dal gruppo mytable per col1, col2
darebbe i seguenti risultati:

Col1  Col2 Col3
a      b    4
a      c    1
b      b    2

Come interpreterebbe
seleziona col1, col2, sum (col3) dal gruppo mytable per col1

La mia ipotesi sarebbe

Col1  Col2 Col3
a      b    5
a      c    5
b      b    2

Questi sono chiaramente risultati negativi. Ovviamente più complessa è la query e più si unisce, meno è probabile che la query restituisca risultati corretti o che il programmatore sappia anche se sono errati.

Personalmente sono contento che raggruppa per richieda i campi.

Sono d'accordo con GROUP BY ALL, GROUP BY * o qualcosa di simile. Come menzionato nel post originale, nel 99% (forse più) dei casi che desideri raggruppare per tutte le colonne / espressioni non aggregate.

Ecco un esempio in cui è necessario disporre di colonne GROUP BY, per motivi di compatibilità con le versioni precedenti.

SELECT 
  MIN(COUNT(*)) min_same_combination_cnt, 
  MAX(COUNT(*)) max_same_comb_cnt, 
  AVG(COUNT(*)) avg_same_comb_cnt, 
  SUM(COUNT(*)) total_records,
  COUNT(COUNT(*)) distinct_combinations_cnt
FROM <some table>
GROUP BY <list of columns>

Funziona in Oracle. Lo uso per stimare la selettività su colonne. Il gruppo di viene applicato alla funzione aggregata interna. Quindi, viene applicato l'aggregato esterno.

Sarebbe bello presentare un suggerimento per questo miglioramento allo standard SQL. Semplicemente non so come funzioni.

In realtà, non sarebbe il 100% delle volte? Esiste un caso in cui è possibile avere una colonna (non aggregata) nella selezione che non si trova in GROUP BY?

Non ho una risposta però. Certamente sembra un momento imbarazzante per la lingua.

Condivido l'opinione dell'op che secondo cui ripetere è un po 'fastidioso, specialmente se i campi non aggregati contengono istruzioni elaborate come if e funzioni e molte altre cose. Sarebbe bello se ci fosse una scorciatoia nel gruppo per clausola - almeno un alias di colonna. Fare riferimento alle colonne in base al numero può essere un'altra opzione, anche se probabilmente ha i propri problemi.

Potrebbe esserci una situazione in cui è necessario estrarre un ID di tutte le righe raggruppate e la somma delle loro quantità, ad esempio. In questo caso, dovresti raggrupparli per nome e lasciare gli ID non raggruppati. SQLite sembra funzionare in questo modo.

Dal momento che raggruppa per risultato in una singola tupla per un intero gruppo di tuple, quindi altri non raggruppati per attributi devono essere usati solo nella funzione aggregata. Se aggiungi non gruppo per attributo in select, sql non può decidere quale valore selezionare da quel gruppo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top