Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Thứ tự truy vấn MySQL theo hầu hết các trường đã hoàn thành

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 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ự.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Nhân bản MySQL:nếu tôi không chỉ định bất kỳ cơ sở dữ liệu nào, thì log_bin có ghi lại MỌI THỨ?

  2. Xuất Cơ sở dữ liệu MySQL sang Cơ sở dữ liệu SQLite

  3. Cách chạy tập lệnh PHP vào thời gian đã lên lịch

  4. Tối ưu hóa truy vấn MySQL với nhiều phép nối bên trái

  5. Tại sao giới hạn thời gian tối đa của MySQL là 838:59:59?