Pokud vím, MySQL nemá žádnou funkci pro počítání nenulových polí na řádku.
Takže jediný způsob, který mě napadá, je použít explicitní podmínku:
SELECT * FROM mytable
ORDER BY (IF( column1 IS NULL, 0, 1)
+IF( column2 IS NULL, 0, 1)
...
+IF( column45 IS NULL, 0, 1)) DESC;
...je to ošklivé jako hřích, ale mělo by to stačit.
Můžete také navrhnout TRIGGER pro zvýšení dalšího sloupce "fields_filled". Spouštěč vás stojí UPDATE
, 45 IF vás bolí na SELECT
; budete muset modelovat, co je pohodlnější.
Všimněte si, že indexování všech polí urychlí SELECT
vás bude při aktualizaci stát (a 45 různých indexů pravděpodobně stojí tolik jako skenování tabulky na select, neříkám, že indexované pole je VARCHAR
). Proveďte nějaké testy, ale věřím, že řešení 45-IF bude pravděpodobně celkově nejlepší.
AKTUALIZACE :Pokud můžete přepracovat strukturu tabulky, abyste ji trochu normalizovali, můžete pole vložit do my_values
stůl. Pak byste měli "tabulku záhlaví" (možná pouze s jedinečným ID) a "tabulku dat". Prázdná pole by vůbec neexistovala a pak byste je mohli seřadit podle počtu vyplněných polí pomocí RIGHT JOIN
, počítaje vyplněná pole pomocí COUNT()
. To by také výrazně urychlilo UPDATE
operací a umožní vám efektivně využívat indexy.
PŘÍKLAD (od nastavení tabulky po nastavení dvou normalizovaných tabulek) :
Řekněme, že máme sadu Customer
evidence. Budeme mít krátkou podmnožinu „povinných“ údajů, jako je ID, uživatelské jméno, heslo, e-mail atd.; pak budeme mít možná mnohem větší podmnožinu „nepovinných“ údajů, jako je přezdívka, avatar, datum narození a tak dále. Jako první krok předpokládejme, že všechna tato data jsou varchar
(toto na první pohled vypadá jako omezení ve srovnání s řešením s jednou tabulkou, kde každý sloupec může mít svůj vlastní datový typ).
Takže máme tabulku jako,
ID username ....
1 jdoe etc.
2 jqaverage etc.
3 jkilroy etc.
Pak máme tabulku nepovinných dat. Zde John Doe vyplnil všechna pole, Joe Q. Průměrně jen dvě a Kilroy žádné (i když byl zde).
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
Abychom mohli reprodukovat výstup "single table" v MySQL, musíme vytvořit poměrně složitý VIEW
se spoustou LEFT JOIN
s. Toto zobrazení bude nicméně velmi rychlé, pokud budeme mít index založený na (userid, var)
(ještě lepší, když použijeme číselnou konstantu nebo SET místo varchar pro datový typ 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)
;
Každé pole v našem logickém modelu, např. „name“, bude obsaženo v n-tici ( id, 'name', value ) ve volitelné datové tabulce.
A získá řádek ve tvaru <FIELDNAME>s.val AS <FIELDNAME>
v sekci (1) výše uvedeného dotazu s odkazem na řádek ve tvaru LEFT JOIN userdata AS <FIELDNAME>s ON ( users.id = <FIELDNAME>s.id AND <FIELDNAME>s.var = '<FIELDNAME>')
v sekci (2). Dotaz tedy můžeme sestavit dynamicky zřetězením prvního textového řádku výše uvedeného dotazu s dynamickou sekcí 1, textem 'FROM users' a dynamicky vytvořenou sekcí 2.
Jakmile to uděláme, SELECTy v pohledu jsou přesně identické jako dříve – ale nyní načítají data ze dvou normalizovaných tabulek prostřednictvím JOINů.
EXPLAIN SELECT * FROM usertable;
nám řekne, že přidání sloupců do tohoto nastavení znatelně nezpomalí operace, tj. toto řešení se poměrně dobře škáluje.
Bude nutné upravit VLOŽENÍ (vkládáme pouze povinné údaje, a to pouze v první tabulce) a také AKTUALIZACE:AKTUALIZUJEME buď tabulku povinných údajů, nebo jeden řádek tabulky nepovinných údajů. Ale pokud tam cílový řádek není, musí být INSERTed.
Musíme tedy vyměnit
UPDATE usertable SET name = 'John Doe', born = 'New York' WHERE id = 1;
s 'upsert', v tomto případě
INSERT INTO userdata VALUES
( 1, 'name', 'John Doe' ),
( 1, 'born', 'New York' )
ON DUPLICATE KEY UPDATE val = VALUES(val);
(Potřebujeme UNIQUE INDEX on userdata(id, var)
pro ON DUPLICATE KEY
do práce).
V závislosti na velikosti řádku a problémech s diskem může tato změna přinést znatelné zvýšení výkonu.
Všimněte si, že pokud tato úprava neprovedete, stávající dotazy nevyvolají chyby – v tichosti selžou .
Zde například upravíme jména dvou uživatelů; jeden má v záznamu jméno, druhý má NULL. První je upraven, druhý nikoli.
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)
Abychom zjistili hodnocení každého řádku, u uživatelů, kteří mají hodnocení, jednoduše načteme počet řádků uživatelských dat na ID:
SELECT id, COUNT(*) AS rank FROM userdata GROUP BY id
Nyní, abychom extrahovali řádky v pořadí „vyplněný stav“, děláme:
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;
LEFT JOIN
zajišťuje, že jsou načteni i bezúhonní jedinci a další řazení podle id
zajišťuje, že lidé se stejnou hodností vždy vyjdou ve stejném pořadí.