Domanda

Sto cercando di aggiungere funzionalità a un'applicazione preesistente e mi sono imbattuto in una vista MySQL simile a questa:

SELECT
     AVG(table_name.col1),
     AVG(table_name.col2),
     AVG(table_name.col3),
     table_name.personID,
     table_name.col4
FROM table_name
GROUP BY table_name.personID;

OK, quindi ci sono alcune funzioni aggregate. Puoi selezionare personID perché lo stai raggruppando. Ma sta anche selezionando una colonna che non è in una funzione aggregata e non fa parte della clausola GROUP BY. Com'è possibile??? Seleziona semplicemente un valore casuale perché i valori sicuramente non sono univoci per gruppo?

Da dove vengo (MSSQL Server), è un errore. Qualcuno può spiegarmi questo comportamento e perché è consentito in MySQL?

È stato utile?

Soluzione

È vero che questa funzione consente alcune query ambigue e restituisce silenziosamente un set di risultati con un valore arbitrario prelevato da quella colonna. In pratica, tende ad essere il valore della riga all'interno del gruppo che viene archiviato fisicamente per primo.

Queste query non sono ambigue se si scelgono solo colonne che sono funzionalmente dipendenti dalle colonne nei criteri GROUP BY. In altre parole, se può esserci un solo valore distinto di "ambiguo" colonna per valore che definisce il gruppo, non c'è problema. Questa query sarebbe illegale in Microsoft SQL Server (e ANSI SQL), anche se non può logicamente generare ambiguità:

SELECT AVG(table1.col1), table1.personID, persons.col4
FROM table1 JOIN persons ON (table1.personID = persons.id)
GROUP BY table1.personID;

Inoltre, MySQL ha una modalità SQL per farlo funzionare secondo lo standard: ONLY_FULL_GROUP_BY

FWIW, SQLite consente anche queste ambigue clausole GROUP BY, ma sceglie il valore dall'ultima ultima riga nel gruppo. & # 8224;


& # 8224; Almeno nella versione che ho provato. Ciò che significa essere arbitrario è che MySQL o SQLite potrebbero cambiare la loro implementazione in futuro e avere un comportamento diverso. Pertanto, non dovresti fare affidamento sul comportamento nel modo in cui si trova attualmente in casi ambigui come questo. È meglio riscrivere le tue domande in modo che siano deterministiche e non ambigue. Ecco perché MySQL 5.7 ora abilita ONLY_FULL_GROUP_BY per impostazione predefinita.

Altri suggerimenti

Avrei dovuto cercare su Google per un po 'di più ... Sembra di aver trovato la mia risposta .

  

MySQL estende così l'utilizzo di GROUP BY   che è possibile utilizzare colonne non aggregate   o calcoli nell'elenco SELEZIONA   che non compaiono in GROUP BY   clausola. È possibile utilizzare questa funzione per   ottenere prestazioni migliori evitando   ordinamento di colonne non necessario e   raggruppamento. Ad esempio, non è necessario   raggruppare su customer.name in   seguente query

     

In SQL standard, dovresti aggiungere   customer.name alla clausola GROUP BY.   In MySQL, il nome è ridondante.

Comunque, sembra proprio ... sbagliato.

select * from personel where p_id IN(select
min(dbo.personel.p_id)
FROM
personel
GROUP BY dbo.personel.p_adi)

Supponiamo che tu abbia una domanda come questa:

SELECT g, v 
FROM t
GROUP BY g;

In questo caso, per ogni possibile valore per g , mysql seleziona uno dei valori corrispondenti di v .

Tuttavia, quale viene scelto, dipende da alcune circostanze.

Ho letto da qualche parte che per ogni gruppo di g viene mantenuto il primo valore di v , nell'ordine in cui i record sono stati inseriti nella tabella t .

Questo è abbastanza brutto perché i record in una tabella dovrebbero essere trattati come un set in cui l'ordine degli elementi non dovrebbe avere importanza. Questo è così " mysql-ish " ...

Se vuoi determinare quale valore mantenere v , devi applicare una sottoselezione per t in questo modo:

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        ORDER BY g, v DESC
) q
GROUP BY g;

In questo modo definisci l'ordine in cui i record della sottoquery vengono elaborati dalla query esterna, quindi puoi fidarti del valore di v che sceglierà per i singoli valori di g .

Tuttavia, se hai bisogno di alcune condizioni WHERE, fai molta attenzione. Se aggiungi la condizione WHERE alla sottoquery, manterrà il comportamento, restituirà sempre il valore che ti aspetti:

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
GROUP BY g;

Questo è ciò che ti aspetti, la sottoselezione filtra e ordina la tabella. Mantiene i record in cui g ha il valore dato e la query esterna restituisce g e il primo valore per v .

Tuttavia, se si aggiunge la stessa condizione WHERE alla query esterna, si ottiene un risultato non deterministico:

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        -- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
GROUP BY g;

Sorprendentemente, potresti ottenere valori diversi per v quando esegui la stessa query più e più volte, il che è ... strano. Il comportamento previsto è quello di ottenere tutti i record nell'ordine appropriato dalla sottoquery, filtrandoli nella query esterna e quindi selezionandoli come nell'esempio precedente. Ma non lo fa.

Prende un valore per v apparentemente casualmente. La stessa query ha restituito valori diversi per v se ho eseguito più (~ 20) volte ma la distribuzione non era uniforme.

Se invece di aggiungere un WHERE esterno, specifichi una condizione HAVING come questa:

SELECT g, v 
FROM (
    SELECT * 
        FROM t1 
        -- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
-- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
GROUP BY g
HAVING g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9';

Quindi ottieni di nuovo un comportamento coerente.

CONCLUSIONE: Suggerirei di non fare assolutamente affidamento su questa tecnica. Se si desidera / è necessario evitare le condizioni WHERE nella query esterna. Usalo nella query interna se puoi o una clausola HAVING nella query esterna.

L'ho provato con questi dati:

CREATE TABLE t1 (
    v INT,
    g VARCHAR(36)
);

INSERT INTO t1 VALUES (1, '737a8783-110c-447e-b4c2-1cbb7c6b72c9');
INSERT INTO t1 VALUES (2, '737a8783-110c-447e-b4c2-1cbb7c6b72c9');

in mysql 5.6.41.

Forse è solo un bug che viene / è stato corretto nelle versioni più recenti, si prega di dare un feedback se si ha esperienza con le versioni più recenti.

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