Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Urutan kueri MySQL berdasarkan bidang yang paling lengkap

MySQL tidak memiliki fungsi untuk menghitung jumlah bidang non-NULL pada satu baris, sejauh yang saya tahu.

Jadi satu-satunya cara yang bisa saya pikirkan adalah menggunakan kondisi eksplisit:

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

... itu jelek seperti dosa, tetapi harus melakukan trik.

Anda juga dapat merancang PEMICU untuk menambah kolom tambahan "fields_filled". Pemicu dikenakan biaya pada UPDATE , 45 JIKA menyakiti Anda di SELECT; Anda harus membuat model yang lebih nyaman.

Perhatikan bahwa mengindeks semua bidang untuk mempercepat SELECT akan dikenakan biaya saat memperbarui (dan 45 indeks berbeda mungkin berharga sama seperti pemindaian tabel pada pilihan, belum lagi bidang yang diindeks adalah VARCHAR ). Jalankan beberapa pengujian, tetapi saya yakin bahwa solusi 45-IF kemungkinan besar akan menjadi yang terbaik secara keseluruhan.

PERBARUI :Jika Anda dapat mengerjakan ulang struktur tabel Anda untuk menormalkannya, Anda dapat meletakkan bidang dalam my_values meja. Maka Anda akan memiliki "tabel tajuk" (mungkin hanya dengan ID unik) dan "tabel data". Kolom kosong tidak akan ada sama sekali, lalu Anda dapat mengurutkan berdasarkan jumlah kolom yang terisi dengan menggunakan RIGHT JOIN , menghitung bidang yang diisi dengan COUNT() . Ini juga akan sangat mempercepat UPDATE operasi, dan akan memungkinkan Anda menggunakan indeks secara efisien.

CONTOH (dari penyiapan tabel hingga dua penyiapan tabel yang dinormalisasi) :

Katakanlah kita memiliki satu set Customer catatan. Kami akan memiliki subset singkat dari data "wajib" seperti ID, nama pengguna, kata sandi, email, dll.; maka kita akan memiliki subset data "opsional" yang mungkin jauh lebih besar seperti nama panggilan, avatar, tanggal lahir, dan sebagainya. Sebagai langkah pertama mari kita asumsikan bahwa semua data ini adalah varchar (ini, pada pandangan pertama, terlihat seperti batasan jika dibandingkan dengan solusi tabel tunggal di mana setiap kolom mungkin memiliki tipe datanya sendiri).

Jadi kita punya tabel seperti,

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

Kemudian kita memiliki tabel data-opsional. Di sini John Doe telah mengisi semua bidang, Joe Q. Rata-rata hanya dua, dan Kilroy tidak ada (bahkan jika dia adalah di sini).

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

Untuk mereproduksi output "tabel tunggal" di MySQL, kita harus membuat VIEW yang cukup rumit dengan banyak LEFT JOIN s. Tampilan ini tetap akan sangat cepat jika kita memiliki indeks berdasarkan (userid, var) (bahkan lebih baik jika kita menggunakan konstanta numerik atau SET daripada varchar untuk tipe data 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)
;

Setiap bidang dalam model logis kami, misalnya, "nama", akan dimuat dalam sebuah tuple ( id, 'name', value ) dalam tabel data opsional.

Dan akan menghasilkan baris dengan bentuk <FIELDNAME>s.val AS <FIELDNAME> di bagian (1) kueri di atas, mengacu pada baris formulir LEFT JOIN userdata AS <FIELDNAME>s ON ( users.id = <FIELDNAME>s.id AND <FIELDNAME>s.var = '<FIELDNAME>') pada ayat (2). Jadi kita dapat membuat kueri secara dinamis dengan menggabungkan baris teks pertama dari kueri di atas dengan Bagian 1 dinamis, teks 'FROM users ' dan Bagian 2 yang dibuat secara dinamis.

Setelah kita melakukan ini, SELECT pada tampilan sama persis dengan sebelumnya -- tetapi sekarang mereka mengambil data dari dua tabel yang dinormalisasi melalui GABUNG.

EXPLAIN SELECT * FROM usertable;

akan memberi tahu kami bahwa menambahkan kolom ke penyiapan ini tidak memperlambat operasi yang berarti, yaitu, solusi ini berskala cukup baik.

INSERT harus dimodifikasi (kami hanya memasukkan data wajib, dan hanya di tabel pertama) dan juga UPDATE:kami MEMPERBARUI tabel data wajib, atau satu baris tabel data opsional. Tetapi jika baris target tidak ada, maka harus DIMASUKKAN.

Jadi kita harus mengganti

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

dengan 'upsert', dalam hal ini

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

(Kami membutuhkan UNIQUE INDEX on userdata(id, var) untuk ON DUPLICATE KEY untuk bekerja).

Bergantung pada ukuran baris dan masalah disk, perubahan ini mungkin menghasilkan peningkatan kinerja yang cukup besar.

Perhatikan bahwa jika modifikasi ini tidak dilakukan, kueri yang ada tidak akan menghasilkan kesalahan - mereka akan gagal secara diam-diam .

Di sini misalnya kita memodifikasi nama dua pengguna; satu memang memiliki nama dalam catatan, yang lain memiliki NULL. Yang pertama dimodifikasi, yang kedua tidak.

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)

Untuk mengetahui peringkat setiap baris, bagi pengguna yang memiliki peringkat, kita cukup mengambil jumlah baris data pengguna per id:

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

Sekarang untuk mengekstrak baris dalam urutan "status terisi", kita lakukan:

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 memastikan bahwa individu tanpa pangkat juga diambil, dan pemesanan tambahan dengan id memastikan bahwa orang dengan peringkat yang sama selalu keluar dalam urutan yang sama.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Perintah Mysql tidak ditemukan di OS X 10.7

  2. SQL menghitung nilai tertentu pada beberapa kolom dan baris

  3. SQL, Inner join, ganti id dengan nama

  4. kesalahan saat menginstal mysqlclient menggunakan pip ke MacOS Bigsur

  5. Pindah dari MySQL 5.7 ke MySQL 8.0 - Yang Harus Anda Ketahui