Mysql
 sql >> база данни >  >> RDS >> Mysql

Подредба на MySQL заявките по най-попълнените полета

MySQL няма функция за преброяване на броя не-NULL полета в ред, доколкото знам.

Така че единственият начин, за който се сещам, е да използвам изрично условие:

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

...грозно е като грях, но трябва да свърши работа.

Можете също така да създадете TRIGGER за увеличаване на допълнителна колона "fields_filled". Тригерът ви струва на UPDATE , 45-те IF-а ви нараняват при SELECT; ще трябва да моделирате това, което е по-удобно.

Имайте предвид, че индексирането на всички полета за ускоряване на SELECT ще ви струва при актуализиране (и 45 различни индекса вероятно струват колкото сканиране на таблица при избрано, да не казваме, че индексираното поле е VARCHAR ). Направете някои тестове, но вярвам, че решението 45-IF вероятно ще бъде най-доброто като цяло.

АКТУАЛИЗАЦИЯ :Ако можете да преработите структурата на таблицата си, за да я нормализирате донякъде, можете да поставите полетата в my_values маса. Тогава ще имате "заглавна таблица" (може би само с уникален идентификатор) и "таблица с данни". Празните полета изобщо няма да съществуват и тогава можете да сортирате по колко попълнени полета има, като използвате RIGHT JOIN , преброявайки попълнените полета с COUNT() . Това също би ускорило значително UPDATE операции и ще ви позволи да използвате ефективно индекси.

ПРИМЕР (от настройка на таблица до настройка на две нормализирани таблици) :

Да кажем, че имаме набор от Customer записи. Ще имаме кратък поднабор от „задължителни“ данни като ID, потребителско име, парола, имейл и т.н.; тогава ще имаме може би много по-голямо подмножество от „незадължителни“ данни като псевдоним, аватар, дата на раждане и т.н. Като първа стъпка нека приемем, че всички тези данни са varchar (това на пръв поглед изглежда като ограничение в сравнение с решението с една таблица, където всяка колона може да има свой собствен тип данни).

Така че имаме таблица като,

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

След това имаме таблицата с незадължителни данни. Тук Джон Доу е попълнил всички полета, Джо Кю. Средно само две, а Килрой нито едно (дори ако беше тук).

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

За да възпроизведем изхода "единична таблица" в MySQL, трябва да създадем доста сложен VIEW с много LEFT JOIN с. Въпреки това този изглед ще бъде много бърз, ако имаме индекс, базиран на (userid, var) (още по-добре, ако използваме числова константа или SET вместо varchar за типа данни на 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)
;

Всяко поле в нашия логически модел, например "име", ще се съдържа в кортеж ( id, 'name', value ) в незадължителната таблица с данни.

И ще се получи ред във формата <FIELDNAME>s.val AS <FIELDNAME> в раздел (1) на горната заявка, препращайки към ред във формата LEFT JOIN userdata AS <FIELDNAME>s ON ( users.id = <FIELDNAME>s.id AND <FIELDNAME>s.var = '<FIELDNAME>') в раздел (2). Така че можем да конструираме заявката динамично, като свържем първия текстов ред на горната заявка с динамична секция 1, текста „ОТ потребители“ и динамично изградена секция 2.

След като направим това, SELECT в изгледа са напълно идентични с предишните -- но сега те извличат данни от две нормализирани таблици чрез JOIN.

EXPLAIN SELECT * FROM usertable;

ще ни каже, че добавянето на колони към тази настройка не забавя значително операциите, т.е. това решение се мащабира сравнително добре.

Вмъкванията ще трябва да бъдат модифицирани (ние вмъкваме само задължителни данни и само в първата таблица), както и АКТУАЛИЗИРАНЕ:ние или АКТУАЛИЗИРАМЕ таблицата със задължителни данни, или един ред от незадължителната таблица с данни. Но ако целевият ред не е там, тогава той трябва да бъде ВМЪКЕН.

Така че трябва да заменим

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

с 'upsert', в този случай

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

(Нуждаем се от UNIQUE INDEX on userdata(id, var) за ON DUPLICATE KEY на работа).

В зависимост от размера на реда и проблемите с диска, тази промяна може да доведе до значително увеличение на производителността.

Обърнете внимание, че ако тази модификация не бъде извършена, съществуващите заявки няма да дадат грешки - те тихо ще се провалят .

Тук например променяме имената на двама потребители; единият има записано име, другият има NULL. Първият е модифициран, вторият не.

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)

За да знаем ранга на всеки ред, за тези потребители, които имат ранг, ние просто извличаме броя на редовете с потребителски данни за id:

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

Сега, за да извлечем редове в реда на „попълнен статус“, ние правим:

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 гарантира, че лицата без ранг също ще бъдат извлечени и допълнителното подреждане по id гарантира, че хората с еднакъв ранг винаги излизат в един и същ ред.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Най-добри практики:Проследяване на импресии на банер

  2. MYSQL Родител Дете Същата таблица; PHP Nest деца в родители като многоизмерен масив

  3. Zend\Db:Изберете от подзаявка

  4. Работа с MySQL Zero Date с EF Core

  5. mysql - проблем с изместването