sql >> Database >  >> RDS >> Mysql

MySQL-queryvolgorde op meest ingevulde velden

MySQL heeft geen functie om het aantal niet-NULL-velden op een rij te tellen, voor zover ik weet.

Dus de enige manier die ik kan bedenken is om een ​​expliciete voorwaarde te gebruiken:

SELECT * FROM mytable
    ORDER BY (IF( column1 IS NULL, 0, 1)
             +IF( column2 IS NULL, 0, 1)
             ...
             +IF( column45 IS NULL, 0, 1)) DESC;

...het is zo lelijk als de zonde, maar het zou moeten lukken.

Je zou ook een TRIGGER kunnen bedenken om een ​​extra kolom "fields_filled" te verhogen. De trigger kost je op UPDATE , de 45 IF's doen je pijn op SELECT; je zult moeten modelleren wat handiger is.

Merk op dat het indexeren van alle velden om SELECT . te versnellen kost u bij het bijwerken (en 45 verschillende indexen kosten waarschijnlijk net zoveel als een tabelscan bij selecteren, om niet te zeggen dat het geïndexeerde veld een VARCHAR is ). Voer wat tests uit, maar ik denk dat de 45-IF-oplossing over het algemeen waarschijnlijk de beste is.

UPDATE :Als je kunt je tabelstructuur herwerken om het enigszins te normaliseren, je zou de velden in een my_values kunnen plaatsen tafel. Dan heb je een "header table" (misschien met alleen een unieke ID) en een "data table". Lege velden zouden helemaal niet bestaan, en dan zou je kunnen sorteren op hoeveel ingevulde velden er zijn door een RIGHT JOIN te gebruiken , waarbij de ingevulde velden worden geteld met COUNT() . Dit zou ook UPDATE aanzienlijk versnellen operaties, en zou u in staat stellen om indexen efficiënt te gebruiken.

VOORBEELD (van tafelopstelling naar twee genormaliseerde tafelsopstelling) :

Laten we zeggen dat we een set van Customer . hebben verslagen. We hebben een korte subset van "verplichte" gegevens zoals ID, gebruikersnaam, wachtwoord, e-mail, enz.; dan hebben we misschien een veel grotere subset van "optionele" gegevens zoals bijnaam, avatar, geboortedatum, enzovoort. Laten we als eerste stap aannemen dat al deze gegevens varchar . zijn (dit lijkt op het eerste gezicht een beperking in vergelijking met de oplossing met één tabel waarbij elke kolom zijn eigen gegevenstype kan hebben).

Dus we hebben een tabel als,

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

Dan hebben we de tabel met optionele gegevens. Hier heeft John Doe alle velden ingevuld, Joe Q. Gemiddeld slechts twee, en Kilroy geen (zelfs als hij was hier).

userid  var   val
1       name  John
1       born  Stratford-upon-Avon
1       when  11-07-1974
2       name  Joe Quentin
2       when  09-04-1962

Om de uitvoer van de "enkele tabel" in MySQL te reproduceren, moeten we een vrij complexe VIEW maken met veel LEFT JOIN s. Deze weergave zal niettemin erg snel zijn als we een index hebben gebaseerd op (userid, var) (nog beter als we een numerieke constante of een SET gebruiken in plaats van een varchar voor het datatype van var :

CREATE OR REPLACE VIEW usertable AS SELECT users.*,
    names.val AS name // (1)
FROM users
    LEFT JOIN userdata AS names ON ( users.id = names.id AND names.var = 'name') // (2)
;

Elk veld in ons logisch model, bijv. "naam", zal worden opgenomen in een tuple ( id, 'naam', waarde ) in de optionele gegevenstabel.

En het levert een regel op van de vorm <FIELDNAME>s.val AS <FIELDNAME> in de sectie (1) van de bovenstaande query, verwijzend naar een regel van de vorm LEFT JOIN userdata AS <FIELDNAME>s ON ( users.id = <FIELDNAME>s.id AND <FIELDNAME>s.var = '<FIELDNAME>') in sectie (2). We kunnen de query dus dynamisch construeren door de eerste tekstregel van de bovenstaande query samen te voegen met een dynamische sectie 1, de tekst 'FROM users' en een dynamisch gebouwde sectie 2.

Zodra we dit doen, zijn SELECT's in de weergave exact identiek aan voorheen -- maar nu halen ze gegevens op uit twee genormaliseerde tabellen via JOIN's.

EXPLAIN SELECT * FROM usertable;

zal ons vertellen dat het toevoegen van kolommen aan deze opstelling de bewerkingen niet aanzienlijk vertraagt, d.w.z. deze oplossing schaalt redelijk goed.

INSERT's moeten worden aangepast (we voegen alleen verplichte gegevens in, en alleen in de eerste tabel) en UPDATE's ook:we UPDATEN de verplichte gegevenstabel, of een enkele rij van de optionele gegevenstabel. Maar als de doelrij er niet is, moet deze worden INGEVOERD.

Dus we moeten vervangen

UPDATE usertable SET name = 'John Doe', born = 'New York' WHERE id = 1;

met een 'upsert', in dit geval

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

(We hebben een UNIQUE INDEX on userdata(id, var) voor ON DUPLICATE KEY aan het werk).

Afhankelijk van de rijgrootte en schijfproblemen, kan deze wijziging een aanzienlijke prestatiewinst opleveren.

Houd er rekening mee dat als deze wijziging niet wordt uitgevoerd, de bestaande zoekopdrachten geen fouten zullen opleveren - ze zullen stilletjes mislukken .

Hier wijzigen we bijvoorbeeld de namen van twee gebruikers; de ene heeft een geregistreerde naam, de andere heeft NULL. De eerste is gewijzigd, de tweede niet.

mysql> SELECT * FROM usertable;
+------+-----------+-------------+------+------+
| id   | username  | name        | born | age  |
+------+-----------+-------------+------+------+
|    1 | jdoe      | John Doe    | NULL | NULL |
|    2 | jqaverage | NULL        | NULL | NULL |
|    3 | jtkilroy  | NULL        | NULL | NULL |
+------+-----------+-------------+------+------+
3 rows in set (0.00 sec)
mysql> UPDATE usertable SET name = 'John Doe II' WHERE username = 'jdoe';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
mysql> UPDATE usertable SET name = 'James T. Kilroy' WHERE username = 'jtkilroy';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0  Changed: 0  Warnings: 0
mysql> select * from usertable;
+------+-----------+-------------+------+------+
| id   | username  | name        | born | age  |
+------+-----------+-------------+------+------+
|    1 | jdoe      | John Doe II | NULL | NULL |
|    2 | jqaverage | NULL        | NULL | NULL |
|    3 | jtkilroy  | NULL        | NULL | NULL |
+------+-----------+-------------+------+------+
3 rows in set (0.00 sec)

Om de rangorde van elke rij te kennen, voor die gebruikers die wel een rangorde hebben, halen we eenvoudig het aantal gebruikersgegevensrijen per id op:

SELECT id, COUNT(*) AS rank FROM userdata GROUP BY id

Om nu rijen in de volgorde "gevulde status" te extraheren, doen we:

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

De LEFT JOIN zorgt ervoor dat ook gewetenloze individuen worden teruggevonden, en de extra bestelling door id zorgt ervoor dat mensen met een identieke rang altijd in dezelfde volgorde uitkomen.




  1. Hoe kan ik SQL schrijven voor een tabel die dezelfde naam heeft als een beveiligd sleutelwoord in MySql?

  2. Selecteer een MySQL-database op Linux via de opdrachtregel

  3. mysql/php:laat berichten zien en voor elk bericht alle opmerkingen

  4. DOE MEE met drie tafels