Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Kolejność zapytań MySQL według większości wypełnionych pól

MySQL nie ma funkcji do zliczania liczby pól innych niż NULL w wierszu, o ile wiem.

Więc jedynym sposobem, jaki przychodzi mi do głowy, jest użycie wyraźnego warunku:

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

...jest brzydkie jak grzech, ale powinno wystarczyć.

Możesz również wymyślić TRIGGER, aby zwiększyć dodatkową kolumnę „fields_filled”. Wyzwalacz kosztuje Cię w dniu UPDATE , 45 IF zaszkodziło Ci przy SELECT; będziesz musiał modelować, co jest wygodniejsze.

Pamiętaj, że indeksowanie wszystkich pól przyspiesza SELECT będzie cię kosztować podczas aktualizacji (a 45 różnych indeksów prawdopodobnie kosztuje tyle, co skanowanie tabeli przy wyborze, nie mówiąc, że indeksowane pole to VARCHAR ). Przeprowadź kilka testów, ale uważam, że rozwiązanie 45-IF będzie prawdopodobnie najlepsze ogólnie.

AKTUALIZUJ :Jeśli możesz przerobić strukturę tabeli, aby nieco ją znormalizować, możesz umieścić pola w moje_wartości stół. Wtedy będziesz miał „tablicę nagłówków” (może z tylko unikalnym identyfikatorem) i „tablicę danych”. Puste pola w ogóle by nie istniały, a następnie możesz sortować według liczby wypełnionych pól, używając RIGHT JOIN , licząc wypełnione pola za pomocą COUNT() . To również znacznie przyspieszyłoby AKTUALIZACJĘ operacji i pozwoli na efektywne wykorzystanie indeksów.

PRZYKŁAD (od konfiguracji stołu do konfiguracji dwóch znormalizowanych stołów) :

Powiedzmy, że mamy zestaw Klienta dokumentacja. Będziemy mieli krótki podzbiór „obowiązkowych” danych, takich jak identyfikator, nazwa użytkownika, hasło, adres e-mail itp.; wtedy będziemy mieli być może znacznie większy podzbiór „opcjonalnych” danych, takich jak pseudonim, awatar, data urodzenia i tak dalej. Jako pierwszy krok załóżmy, że wszystkie te dane są varchar (na pierwszy rzut oka wygląda to na ograniczenie w porównaniu z rozwiązaniem z pojedynczą tabelą, w którym każda kolumna może mieć swój własny typ danych).

Mamy więc taki stół,

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

Następnie mamy tabelę danych opcjonalnych. Tutaj John Doe wypełnił wszystkie pola, Joe Q. Średnio tylko dwa, a Kilroy żadnego (nawet jeśli był tutaj).

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

Aby odtworzyć wynik "pojedynczej tabeli" w MySQL, musimy stworzyć dość złożony VIEW z dużą ilością LEFT JOIN s. Ten widok będzie jednak bardzo szybki, jeśli mamy indeks oparty na (userid, var) (nawet lepiej, jeśli użyjemy stałej numerycznej lub SET zamiast varchar dla typu danych zmienna :

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żde pole w naszym modelu logicznym, np. "name", będzie zawarte w krotce ( id, 'name', value ) w opcjonalnej tabeli danych.

I zwróci wiersz w postaci s.val AS w sekcji (1) powyższego zapytania, odwołując się do wiersza w postaci LEFT JOIN userdata AS s ON ( users.id =s.id AND s.var =' ') w sekcji (2). Możemy więc skonstruować zapytanie dynamicznie, łącząc pierwszą linię tekstu powyższego zapytania z dynamiczną sekcją 1, tekstem „FROM users” i dynamicznie utworzoną sekcją 2.

Gdy to zrobimy, SELECT w widoku są dokładnie takie same jak wcześniej — ale teraz pobierają dane z dwóch znormalizowanych tabel za pomocą JOIN.

EXPLAIN SELECT * FROM usertable;

powie nam, że dodanie kolumn do tej konfiguracji nie spowalnia znacząco operacji, tj. to rozwiązanie skaluje się dość dobrze.

WSTAWKI będą musiały zostać zmodyfikowane (wstawiamy tylko dane obowiązkowe i tylko w pierwszej tabeli) oraz UAKTUALNIEMY:albo AKTUALIZujemy obowiązkową tabelę danych, albo pojedynczy wiersz opcjonalnej tabeli danych. Ale jeśli nie ma tam docelowego wiersza, należy go WSTAWIĆ.

Więc musimy wymienić

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

z 'upsert', w tym przypadku

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

(Potrzebujemy UNIKALNEGO INDEKSU na dane użytkownika(id, var) dla W ZDUPLIKOWANYM KLUCZE do pracy).

W zależności od rozmiaru wiersza i problemów z dyskiem ta zmiana może przynieść znaczny wzrost wydajności.

Pamiętaj, że jeśli ta modyfikacja nie zostanie wykonana, istniejące zapytania nie spowodują błędów — po cichu zakończą się niepowodzeniem .

Tutaj na przykład modyfikujemy nazwy dwóch użytkowników; jeden ma zapisane imię, drugi ma NULL. Pierwsza jest modyfikowana, druga nie.

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)

Aby poznać pozycję każdego wiersza, dla tych użytkowników, którzy mają pozycję, po prostu pobieramy liczbę wierszy danych użytkownika na identyfikator:

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

Teraz, aby wyodrębnić wiersze w kolejności „wypełnione”, robimy:

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;

LEWY DOŁĄCZ zapewnia, że ​​pobierane są również osobniki bez rangą, a dodatkowe uporządkowanie według id zapewnia, że ​​ludzie o identycznej randze zawsze wychodzą w tej samej kolejności.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. 'secure-file-priv' w MySQL nie pozwala na ładowanie pliku danych, nawet z przydzielonego bezpiecznego folderu

  2. Ten wynik jest zbiorem wyników tylko do przodu, wywołanie funkcji rewind() po przejściu do przodu nie jest obsługiwane - Zend

  3. Grupuj według roku w polu daty w MySQL

  4. Używanie wyzwalacza aktualizacji do aktualizacji innej tabeli

  5. Jak uzyskać zliczone wartości z separatorem przecinków jako wartość pojedynczego wiersza?