Zoeken…


Invoering

Onlangs zijn nieuwe versies van MySQL-servers begonnen met het genereren van 1055 fouten voor zoekopdrachten die vroeger werkten. In dit onderwerp worden deze fouten uitgelegd. Het MySQL-team heeft gewerkt om de niet-standaard extensie van GROUP BY te GROUP BY , of op zijn minst om het moeilijker te maken voor ontwikkelaars van het schrijven van zoekopdrachten.

Opmerkingen

MySQL bevat al geruime tijd een beruchte niet-standaard extensie van GROUP BY , die vreemd gedrag mogelijk maakt in naam van efficiëntie. Met deze extensie hebben talloze ontwikkelaars over de hele wereld GROUP BY in productiecode kunnen gebruiken zonder volledig te begrijpen wat ze aan het doen waren.

Het is met name een slecht idee om SELECT * te gebruiken in een GROUP BY query, omdat een standaard GROUP BY clausule het opsommen van de kolommen vereist. Veel ontwikkelaars hebben dat helaas gedaan.

Lees dit. https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

Het MySQL-team heeft geprobeerd dit probleem op te lossen zonder de productiecode te verpesten. Ze voegden een vlag sql_mode in 5.7.5 met de naam ONLY_FULL_GROUP_BY om standaardgedrag ONLY_FULL_GROUP_BY te dwingen. In een recente release hebben ze die vlag standaard ingeschakeld. Toen u uw lokale MySQL opwaardeerde naar 5.7.14, werd de vlag ingeschakeld en werkte uw productiecode, afhankelijk van de oude extensie, niet meer.

Als u recentelijk 1055-fouten krijgt, wat zijn uw keuzes?

  1. repareer de aanstootgevende SQL-vragen, of laat hun auteurs dat doen.
  2. terugdraaien naar een versie van MySQL-compatibel out-of-the-box met de applicatiesoftware die u gebruikt.
  3. wijzig de sql_mode van uw server om de nieuw ingestelde modus ONLY_FULL_GROUP_BY .

U kunt de modus wijzigen met een SET opdracht.

SET  sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'

zou het moeten doen als je het doet direct nadat je applicatie verbinding maakt met MySQL.

Of u kunt het init-bestand in uw MySQL-installatie vinden, de sql_mode= regel vinden en wijzigen om ONLY_FULL_GROUP_BY weg te laten en uw server opnieuw op te starten.

GROUP BY gebruiken en misbruiken

 SELECT item.item_id, item.name,     /* not SQL-92 */ 
        COUNT(*) number_of_uses
  FROM item 
  JOIN uses ON item.item_id, uses.item_id
 GROUP BY item.item_id

toont de rijen in een tabel genaamd item , en toont het aantal gerelateerde rijen in een tabel met de naam uses . Dit werkt goed, maar helaas is het geen standaard SQL-92.

Waarom niet? omdat de SELECT component (en de ORDER BY component) in GROUP BY query's kolommen moeten bevatten die dat zijn

  1. vermeld in de GROUP BY clausule, of
  2. geaggregeerde functies zoals COUNT() , MIN() , en dergelijke.

De SELECT clausule van dit voorbeeld vermeldt item.name , een kolom die aan geen van deze criteria voldoet. MySQL 5.6 en eerder weigert deze zoekopdracht als de SQL-modus ONLY_FULL_GROUP_BY bevat.

Deze voorbeeldquery kan worden gemaakt om te voldoen aan de SQL-92-standaard door de clausule GROUP BY als volgt te wijzigen.

SELECT item.item_id, item.name, 
       COUNT(*) number_of_uses
  FROM item 
  JOIN uses ON item.item_id, uses.item_id
 GROUP BY item.item_id, item.name

Met de latere SQL-99-standaard kan een SELECT instructie niet-samengevoegde kolommen uit de groepssleutel weglaten als het DBMS een functionele afhankelijkheid tussen deze en de groepssleutelkolommen kan aantonen. Omdat item.name functioneel afhankelijk is van item.item_id , is het eerste voorbeeld geldige SQL-99. MySQL kreeg een functionele afhankelijkheidsprover in versie 5.7. Het originele voorbeeld werkt onder ONLY_FULL_GROUP_BY .

GROUP BY misbruiken om onvoorspelbare resultaten terug te geven: de wet van Murphy

SELECT item.item_id, uses.category,   /* nonstandard */ 
       COUNT(*) number_of_uses 
  FROM item 
  JOIN uses ON item.item_id, uses.item_id
 GROUP BY item.item_id

toont de rijen in een tabel genaamd item, en toont het aantal gerelateerde rijen in een tabel met de naam use. Het toont ook de waarde van een kolom met de naam uses.category .

Deze zoekopdracht werkt in MySQL (voordat de vlag ONLY_FULL_GROUP_BY verscheen). Het maakt gebruik van MySQL's niet-standaard extensie voor GROUP BY .

Maar de query heeft een probleem: als meerdere rijen in de uses overeenkomen met de ON voorwaarde in de clausule JOIN , retourneert MySQL de category uit slechts één van die rijen. Welke rij De schrijver van de zoekopdracht en de gebruiker van de toepassing, weten dat niet van tevoren. Formeel gezien is het onvoorspelbaar : MySQL kan elke gewenste waarde retourneren.

Onvoorspelbaar is als willekeurig, met één significant verschil. Je zou verwachten dat een willekeurige keuze van tijd tot tijd verandert. Als een keuze dus willekeurig was, kunt u deze tijdens het opsporen of testen detecteren. Het onvoorspelbare resultaat is slechter: MySQL retourneert hetzelfde resultaat telkens wanneer u de query gebruikt, totdat dit niet het geval is. Soms is het een nieuwe versie van de MySQL-server die een ander resultaat veroorzaakt. Soms is het een groeiende tafel die het probleem veroorzaakt. Wat er mis kan gaan, zal misgaan, en wanneer je het niet verwacht. Dat wordt de wet van Murphy genoemd.

Het MySQL-team heeft gewerkt om het moeilijker te maken voor ontwikkelaars om deze fout te maken. Nieuwere versies van MySQL in de volgorde 5.7 hebben een sql_mode vlag met de naam ONLY_FULL_GROUP_BY . Wanneer die vlag is ingesteld, retourneert de MySQL-server de 1055-fout en weigert dit soort query uit te voeren.

GROUP BY met SELECT * misbruiken en oplossen.

Soms ziet een query er zo uit, met een * in de SELECT clausule.

 SELECT item.*,     /* nonstandard */ 
        COUNT(*) number_of_uses
  FROM item 
  JOIN uses ON item.item_id, uses.item_id
 GROUP BY item.item_id

Een dergelijke zoekopdracht moet opnieuw worden uitgevoerd om te voldoen aan de norm ONLY_FULL_GROUP_BY .

Om dit te doen, moeten we een subquery die gebruik maakt van GROUP BY correct op de terugkeer number_of_uses waarde voor elke item_id . Deze subquery is kort en krachtig, omdat deze alleen naar de uses hoeft te kijken.

                              SELECT item_id, COUNT(*) number_of_uses
                                FROM  uses 
                               GROUP BY item_id

Dan kunnen we meedoen dat subquery met de item tafel.

 SELECT item.*, usecount.number_of_uses
   FROM item
   JOIN (
                              SELECT item_id, COUNT(*) number_of_uses
                                FROM  uses 
                               GROUP BY item_id
        ) usecount ON item.item_id = usecount.item_id

Hierdoor kan de GROUP BY clausule eenvoudig en correct zijn en kunnen we ook de * specifier gebruiken.

Opmerking: verstandige ontwikkelaars vermijden echter in ieder geval het gebruik van de * specifier. Het is meestal beter om de gewenste kolommen in een query te vermelden.

ELKE WAARDE()

 SELECT item.item_id, ANY_VALUE(uses.tag) tag,   
        COUNT(*) number_of_uses
  FROM item 
  JOIN uses ON item.item_id, uses.item_id
 GROUP BY item.item_id

toont de rijen in een tabel met de naam item , het aantal gerelateerde rijen en een van de waarden in de gerelateerde tabel met de naam uses .

Je kunt deze ANY_VALUE() -functie beschouwen als een vreemde, soort geaggregeerde functie. In plaats van een telling, som of maximum te retourneren, geeft het de MySQL-server de opdracht om willekeurig één waarde uit de betreffende groep te kiezen. Het is een manier om Fout 1055 te omzeilen.

Wees voorzichtig wanneer u ANY_VALUE() in query's in productietoepassingen.

Het zou echt SURPRISE_ME() moeten heten. Het retourneert de waarde van een rij in de groep GROUP BY. Welke rij het retourneert is onbepaald. Dat betekent dat het volledig aan de MySQL-server ligt. Formeel geeft het een onvoorspelbare waarde.

De server kiest geen willekeurige waarde, het is nog erger. Het retourneert dezelfde waarde telkens wanneer u de query uitvoert, totdat dit niet het geval is. Het kan veranderen of niet, wanneer een tabel groeit of krimpt, of wanneer de server meer of minder RAM heeft, of wanneer de serverversie verandert, of wanneer Mars in retrograde is (wat dat ook betekent), of helemaal zonder reden.

Je bent gewaarschuwd.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow