MySQL không có chức năng nào để đếm số lượng trường không phải NULL trên một hàng, theo như tôi biết.
Vì vậy, cách duy nhất tôi có thể nghĩ đến là sử dụng một điều kiện rõ ràng:
SELECT * FROM mytable
ORDER BY (IF( column1 IS NULL, 0, 1)
+IF( column2 IS NULL, 0, 1)
...
+IF( column45 IS NULL, 0, 1)) DESC;
... nó xấu như tội lỗi, nhưng nên làm điều đó.
Bạn cũng có thể tạo ra một TRIGGER để tăng thêm một cột "fields_filled". Chi phí kích hoạt của bạn vào UPDATE
, 45 IF làm tổn thương bạn trên SELECT
; bạn sẽ phải lập mô hình những gì thuận tiện hơn.
Lưu ý rằng lập chỉ mục tất cả các trường để tăng tốc SELECT
bạn sẽ phải trả phí khi cập nhật (và 45 chỉ mục khác nhau có thể tốn kém như một lần quét bảng trên lựa chọn, không có nghĩa là trường được lập chỉ mục là VARCHAR
). Chạy một số thử nghiệm, nhưng tôi tin rằng giải pháp 45-IF có thể là giải pháp tổng thể tốt nhất.
CẬP NHẬT : Nếu bạn có thể làm lại cấu trúc bảng của mình để bình thường hóa phần nào, bạn có thể đặt các trường trong my_values
bàn. Sau đó, bạn sẽ có một "bảng tiêu đề" (có thể chỉ với một ID duy nhất) và một "bảng dữ liệu". Các trường trống sẽ hoàn toàn không tồn tại và sau đó bạn có thể sắp xếp theo số lượng trường đã điền ở đó bằng cách sử dụng RIGHT JOIN
, đếm các trường đã điền bằng COUNT()
. Điều này cũng sẽ tăng tốc đáng kể UPDATE
và sẽ cho phép bạn sử dụng các chỉ mục một cách hiệu quả.
VÍ DỤ (từ thiết lập bảng đến thiết lập hai bảng chuẩn hóa) :
Giả sử chúng tôi có một tập hợp Customer
Hồ sơ. Chúng tôi sẽ có một tập hợp con ngắn của dữ liệu "bắt buộc" như ID, tên người dùng, mật khẩu, email, v.v.; thì chúng ta sẽ có một tập hợp con dữ liệu "tùy chọn" có thể lớn hơn nhiều như biệt hiệu, ảnh đại diện, ngày sinh, v.v. Bước đầu tiên, chúng tôi giả định rằng tất cả những dữ liệu này là varchar
(Điều này, ngay từ cái nhìn đầu tiên, có vẻ như là một hạn chế khi so sánh với giải pháp bảng đơn trong đó mỗi cột có thể có kiểu dữ liệu riêng).
Vì vậy, chúng tôi có một bảng như,
ID username ....
1 jdoe etc.
2 jqaverage etc.
3 jkilroy etc.
Sau đó, chúng tôi có bảng dữ liệu tùy chọn. Ở đây John Doe đã điền tất cả các trường, Joe Q. Trung bình chỉ có hai và Kilroy không điền (ngay cả khi anh ấy là tại đây).
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
Để tái tạo đầu ra "bảng đơn" trong MySQL, chúng ta phải tạo một VIEW
khá phức tạp với nhiều LEFT JOIN
S. Tuy nhiên, chế độ xem này sẽ rất nhanh nếu chúng ta có một chỉ mục dựa trên (userid, var)
(thậm chí tốt hơn nếu chúng ta sử dụng hằng số hoặc SET thay vì varchar cho kiểu dữ liệu của 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)
;
Mỗi trường trong mô hình logic của chúng tôi, ví dụ:"tên", sẽ được chứa trong một bộ (id, 'name', value) trong bảng dữ liệu tùy chọn.
Và nó sẽ mang lại một dòng có dạng <FIELDNAME>s.val AS <FIELDNAME>
trong phần (1) của truy vấn trên, đề cập đến một dòng có dạng LEFT JOIN userdata AS <FIELDNAME>s ON ( users.id = <FIELDNAME>s.id AND <FIELDNAME>s.var = '<FIELDNAME>')
trong phần (2). Vì vậy, chúng tôi có thể tạo truy vấn động bằng cách nối dòng văn bản đầu tiên của truy vấn ở trên với Phần 1 động, văn bản 'TỪ người dùng' và Phần 2 được tạo động.
Sau khi chúng tôi thực hiện việc này, các SELECT trên chế độ xem giống hệt như trước đây - nhưng bây giờ chúng tìm nạp dữ liệu từ hai bảng chuẩn hóa thông qua JOIN.
EXPLAIN SELECT * FROM usertable;
sẽ cho chúng tôi biết rằng việc thêm các cột vào thiết lập này không làm chậm các hoạt động đáng kể, tức là giải pháp này có quy mô hợp lý.
CHÈN sẽ phải được sửa đổi (chúng tôi chỉ chèn dữ liệu bắt buộc và chỉ trong bảng đầu tiên) và CẬP NHẬT:chúng tôi CẬP NHẬT bảng dữ liệu bắt buộc hoặc một hàng duy nhất của bảng dữ liệu tùy chọn. Nhưng nếu hàng đích không có ở đó, thì nó phải được CHÈN.
Vì vậy, chúng tôi phải thay thế
UPDATE usertable SET name = 'John Doe', born = 'New York' WHERE id = 1;
với 'upert', trong trường hợp này là
INSERT INTO userdata VALUES
( 1, 'name', 'John Doe' ),
( 1, 'born', 'New York' )
ON DUPLICATE KEY UPDATE val = VALUES(val);
(Chúng tôi cần một UNIQUE INDEX on userdata(id, var)
cho ON DUPLICATE KEY
để làm việc).
Tùy thuộc vào kích thước hàng và vấn đề đĩa, thay đổi này có thể mang lại hiệu suất đáng kể.
Lưu ý rằng nếu sửa đổi này không được thực hiện, các truy vấn hiện tại sẽ không mang lại lỗi - chúng sẽ không thành công một cách thầm lặng .
Ví dụ ở đây, chúng tôi sửa đổi tên của hai người dùng; một cái có tên trong hồ sơ, cái kia có NULL. Điều đầu tiên được sửa đổi, điều thứ hai thì không.
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)
Để biết thứ hạng của mỗi hàng, đối với những người dùng có thứ hạng, chúng tôi chỉ cần truy xuất số lượng hàng dữ liệu người dùng trên mỗi id:
SELECT id, COUNT(*) AS rank FROM userdata GROUP BY id
Bây giờ để trích xuất các hàng theo thứ tự "trạng thái đã lấp đầy", chúng tôi thực hiện:
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
đảm bảo rằng các cá nhân vô hạng cũng được truy xuất và đặt hàng bổ sung theo id
đảm bảo rằng những người có thứ hạng giống nhau luôn xuất hiện theo cùng một thứ tự.