sql >> Database teknologi >  >> RDS >> Mysql

MySQL-forespørgselsrækkefølge efter de fleste udfyldte felter

MySQL har ingen funktion til at tælle antallet af ikke-NULL-felter på en række, så vidt jeg ved.

Så den eneste måde, jeg kan komme i tanke om, er at bruge en eksplicit betingelse:

VÆLG * FRA mintabel BESTIL EFTER (HVIS( kolonne1 ER NULL, 0, 1) +HVIS( kolonne2 ER NULL, 0, 1) ... +HVIS( kolonne45 ER NULL, 0, 1)) DESC;

...det er grimt som synd, men burde gøre tricket.

Du kan også udtænke en TRIGGER for at øge en ekstra kolonne "fields_filled". Udløseren koster dig på OPDATERING , de 45 IF'er skader dig på SELECT; du bliver nødt til at modellere, hvad der er mere praktisk.

Bemærk, at indeksering af alle felter for at fremskynde SELECT vil koste dig ved opdatering (og 45 forskellige indekser koster sandsynligvis lige så meget som en tabelscanning på select, for ikke at sige at det indekserede felt er en VARCHAR ). Kør nogle tests, men jeg tror, ​​at 45-IF-løsningen sandsynligvis vil være den bedste samlet set.

OPDATERING :Hvis du kan omarbejde din tabelstruktur for at normalisere den noget, du kan placere felterne i en my_values bord. Så ville du have en "header-tabel" (måske med kun et unikt ID) og en "datatabel". Tomme felter ville slet ikke eksistere, og så kunne du sortere efter hvor mange udfyldte felter der er ved at bruge en RIGHT JOIN , tæller de udfyldte felter med COUNT() . Dette ville også i høj grad fremskynde OPDATERING operationer, og ville give dig mulighed for effektivt at anvende indekser.

EKSEMPEL (fra tabelopsætning til opsætning af to normaliserede tabeller) :

Lad os sige, at vi har et sæt Kunde optegnelser. Vi vil have en kort delmængde af "obligatoriske" data såsom ID, brugernavn, adgangskode, e-mail osv.; så vil vi have en måske meget større delmængde af "valgfri" data såsom kaldenavn, avatar, fødselsdato og så videre. Lad os som et første trin antage, at alle disse data er varchar (dette ligner ved første øjekast en begrænsning sammenlignet med enkelttabelløsningen, hvor hver kolonne kan have sin egen datatype).

Så vi har et bord som,

ID brugernavn ....1 jdoe etc.2 jqaverage etc.3 jkilroy etc. 

Så har vi den valgfri-data tabel. Her har John Doe udfyldt alle felter, Joe Q. Gennemsnit kun to, og Kilroy ingen (selvom han var her).

brugernavn var val1 navn John1 født Stratford-upon-Avon1 når 11-07-19742 navn Joe Quentin2 når 09-04-1962 

For at reproducere "single table"-outputtet i MySQL er vi nødt til at skabe en ret kompleks VIEW med masser af LEFT JOIN s. Denne visning vil ikke desto mindre være meget hurtig, hvis vi har et indeks baseret på (brugerid, var) (endnu bedre, hvis vi bruger en numerisk konstant eller et SET i stedet for en varchar for datatypen var :

OPRET ELLER ERSTAT SE brugertabel SOM SELECT brugere.*, names.val AS navn // (1)FRA brugere LEFT JOIN brugerdata SOM navne TIL ( users.id =names.id AND names.var ='name ') // (2); 

Hvert felt i vores logiske model, f.eks. "navn", vil være indeholdt i en tuple ( id, 'navn', værdi ) i den valgfri datatabel.

Og det vil give en linje med formen s.val AS i afsnittet (1) i ovenstående forespørgsel, med henvisning til en linje i formen LEFT JOIN userdata AS s ON ( users.id =s.id AND s.var =' ') i § 2. Så vi kan konstruere forespørgslen dynamisk ved at sammenkæde den første tekstlinje i ovenstående forespørgsel med en dynamisk sektion 1, teksten 'FRA brugere' og en dynamisk bygget sektion 2.

Når vi har gjort dette, er SELECT'er på visningen nøjagtigt identiske med tidligere -- men nu henter de data fra to normaliserede tabeller via JOINs.

EXPLAIN SELECT * FRA brugertabel; 

vil fortælle os, at tilføjelse af kolonner til denne opsætning ikke sænker driften mærkbart, dvs. denne løsning skalerer rimeligt godt.

INSERT skal ændres (vi indsætter kun obligatoriske data, og kun i den første tabel) og OPDATERINGER også:vi enten OPDATERET den obligatoriske datatabel eller en enkelt række i den valgfri datatabel. Men hvis målrækken ikke er der, så skal den indsættes.

Så vi er nødt til at erstatte

OPDATERING brugertabel SET navn ='John Doe', born ='New York' WHERE id =1; 

med en 'upsert', i dette tilfælde

INSERT INTO userdata VALUES ( 1, 'name', 'John Doe' ), ( 1, 'born', 'New York' ) ON DUPLICATE KEY UPDATE val =VALUES(val); 

(Vi har brug for et UNIQUE INDEX på brugerdata(id, var) for ON DUPLICATE KEY at arbejde).

Afhængigt af rækkestørrelse og diskproblemer kan denne ændring muligvis give en mærkbar ydelsesforøgelse.

Bemærk, at hvis denne ændring ikke udføres, vil de eksisterende forespørgsler ikke give fejl - de vil stille mislykkes .

Her ændrer vi f.eks. navnene på to brugere; den ene har et navn registreret, den anden har NULL. Den første er ændret, den anden er ikke.

mysql> VÆLG * FRA brugertabel;+------+-----------+-------------+--- ---+------+| id | brugernavn | navn | født | alder |+------+------------+-------------+------+------+ | 1 | jdoe | John Doe | NULL | NULL || 2 | jqaverage | NULL | NULL | NULL || 3 | jtkilroy | NULL | NULL | NULL |+------+-----------------------+-----+ 3 rækker i sæt (0.00 sek.)mysql> OPDATERING brugertabel SET navn ='John Doe II' WHERE brugernavn ='jdoe';Forespørgsel OK, 1 række påvirket (0.00 sek) Rækker matchede:1 Ændret:1 Advarsler:0mysql> OPDATERE brugertabel SET name ='James T. Kilroy' WHERE brugernavn ='jtkilroy';Forespørgsel OK, 0 rækker påvirket (0,00 sek) Matchede rækker:0 Ændret:0 Advarsler:0mysql> vælg * fra brugertabel;+------+ ---------------------+------+------+| id | brugernavn | navn | født | alder |+------+------------+-------------+------+------+ | 1 | jdoe | John Doe II | NULL | NULL || 2 | jqaverage | NULL | NULL | NULL || 3 | jtkilroy | NULL | NULL | NULL |+------+-----------------------+-----+ 3 rækker i sæt (0,00 sek.) 

For at kende rangeringen af ​​hver række, for de brugere, der har en rang, henter vi simpelthen antallet af brugerdatarækker pr. id:

VÆLG id, COUNT(*) SOM rangering FRA brugerdata GROUP BY id 

For nu at udtrække rækker i "udfyldt status" rækkefølge, gør vi:

SELECT usertable.* FROM usertable LEFT JOIN ( SELECT id, COUNT(*) AS rank FROM userdata GROUP BY id ) AS rankingON (usertable.id =ranking.id)ORDER BY rank DESC, id; 

LEFT JOIN sikrer, at rangløse individer også bliver hentet, og den yderligere rækkefølge efter id sikrer, at personer med identisk rang altid kommer ud i samme rækkefølge.




  1. MySql REGEXP for at matche to kommaseparerede strenge

  2. indsæt hvis ikke eksisterer ellers skal du bare vælge i mysql

  3. Sådan vælger du indlejret JSON i SQL Server med OPENJSON

  4. Hvordan får man adgang til databasen på en anden computer ved hjælp af java og jdbc og sql?