2016-07-17 17 views
6

Tôi có truy vấn SQL trích xuất 3 bản ghi mới nhất của mỗi nhóm. Kết quả truy vấn cho MySQL khác với MariaDB. Truy vấn này được thực hiện trong các sqlfiddle dướiTruy vấn cho mã N hàng đầu trên mỗi nhóm hoạt động cho MySQL nhưng kết quả khác nhau cho MariaDB

http://sqlfiddle.com/#!9/c09fe/2

Nội dung của bảng

CREATE TABLE tmp 
    (`mac_addr` varchar(10), `reader_name` varchar(22), `value` numeric, `time_change` datetime) 
; 

INSERT INTO tmp 
    (`mac_addr`, `reader_name`, `value`, `time_change`) 
VALUES 
    ('''B99A88''', '''name_8''', 1, '2016-07-07 19:21:48'), 
    ('''B99A88''', '''own__detect_1''', 1, '2016-06-21 13:30:00'), 
    ('''B99A88''', '''own__temperature_1''', 37.4, '2016-05-04 18:23:03'), 
    ('''B99A88''', '''own__temperature_1''', 29.4, '2016-05-04 18:19:33'), 
    ('''B99A88''', '''own__temperature_1''', 28.4, '2016-05-04 18:17:32'), 
    ('''B99A88''', '''own__temperature_1''', 27.4, '2016-05-04 18:04:08'), 
    ('''B99A88''', '''own__temperature_1''', 21.4, '2016-05-04 15:11:42'), 
    ('''B99A88''', '''own__detect_1''', 0, '2016-04-20 15:22:23'), 
    ('''B99A88''', '''own__detect_1''', 1, '2016-04-15 17:39:52'), 
    ('''B99A88''', '''own__detect_1''', 0, '2016-04-15 17:39:46'), 
    ('''B99A88''', '''own__detect_1''', 1, '2016-04-11 17:34:00'), 
    ('''B99A88''', '''own__detect_1''', 1, '2016-04-11 17:33:00'), 
    ('''B99A88''', '''own__detect_1''', 0, '2016-04-11 17:33:00'), 
    ('''B99A88''', '''own__temperature_1''', 28.4, '2016-04-10 21:20:20'), 
    ('''B99A88''', '''own__temperature_1''', 32.5, '2016-04-10 21:00:00'), 
    ('''B99A88''', '''own__temperature_1''', 34.2, '2016-04-10 11:29:00') 
; 

Query để trích xuất mới nhất 3 hồ sơ của mỗi nhóm.

SELECT mac_addr, reader_name, value, time_change 
FROM (
    SELECT t1.*, 
      IF(@rn = reader_name, @rowno := @rowno + 1, @rowno := 1) AS rowno, 
      @rn := reader_name 
    FROM (
     SELECT * 
      FROM tmp 
     ORDER BY reader_name, time_change DESC 
    ) t1 
    CROSS JOIN (SELECT @rn := null, @rowno := 0) t2 
) t 
WHERE rowno <= 3 

Kết quả khi sử dụng MySQL v5.6 như sau;

mac_addr reader_name    value time_change 
'B99A88' 'name_8'     1  July, 07 2016 19:21:48 
'B99A88' 'own__detect_1'   1  June, 21 2016 13:30:00 
'B99A88' 'own__detect_1'   0  April, 20 2016 15:22:23 
'B99A88' 'own__detect_1'   1  April, 15 2016 17:39:52 
'B99A88' 'own__temperature_1' 37  May, 04 2016 18:23:03 
'B99A88' 'own__temperature_1' 29  May, 04 2016 18:19:33 
'B99A88' 'own__temperature_1' 28  May, 04 2016 18:17:32 

Kết quả MySQL là những gì tôi muốn. Tuy nhiên, tôi đang sử dụng MariaDB và kết quả khác với kết quả MySQL.

Kết quả MariaDB trông như thế này;

mac_addr reader_name    value time_change 
'B99A88' 'name_8'     1  2016-07-07 19:21:48 
'B99A88' 'own__detect_1'   1  2016-06-21 13:30:00 
'B99A88' 'own__temperature_1' 37  2016-05-04 18:23:03 
'B99A88' 'own__temperature_1' 29  2016-05-04 18:19:33 
'B99A88' 'own__temperature_1' 28  2016-05-04 18:17:32 
'B99A88' 'own__detect_1'   0  2016-04-20 15:22:23 
'B99A88' 'own__detect_1'   1  2016-04-15 17:39:52 
'B99A88' 'own__detect_1'   0  2016-04-15 17:39:46 
'B99A88' 'own__temperature_1' 28  2016-04-10 21:20:20 
'B99A88' 'own__temperature_1' 33  2016-04-10 21:00:00 
'B99A88' 'own__temperature_1' 34  2016-04-10 11:29:00 

Làm cách nào để sửa đổi kết quả truy vấn của MariaDB? Việc sử dụng các chức năng cửa sổ trong MariaDB có phải là một ý tưởng hay không?

+0

mysql có giữ nguyên thứ tự từ truy vấn phụ bên trong không? – 1010

Trả lời

3

Các ORDER BY mà bạn đang sử dụng có hai phím:

ORDER BY reader_name, time_change DESC 

Tuy nhiên, các phím này không nhận ra duy nhất mỗi hàng. Do đó, thứ tự các hàng mà các khóa giống nhau không được bảo đảm - thậm chí không phải giữa hai lần chạy truy vấn trên cùng một cơ sở dữ liệu. Giải pháp thông thường là thêm cột id duy nhất làm khóa ORDER BY cuối cùng để mỗi hàng được xác định duy nhất.

Nói chung, trong SQL, ORDER BY không sử dụng loại ổn định. Một loại ổn định là loại giữ nguyên thứ tự khóa ban đầu, khi các phím giống nhau. Lý do rất đơn giản. Bảng SQL và bộ kết quả đại diện cho không có thứ tự bộ. Không có thứ tự ban đầu để giữ.

Nếu bạn có một cột khóa chính, sau đó các ORDER BY sẽ là:

ORDER BY reader_name, time_change DESC, pk 

Phần còn lại của mã không cần phải thay đổi. Bạn chỉ muốn sắp xếp ổn định.

+0

Giả sử có một cột id duy nhất được sử dụng làm khóa chính. Làm thế nào mã nên được sửa đổi để có được kết quả MariaDB bằng MySQL? Hoặc có cách nào tốt hơn để viết truy vấn? –

+1

Tôi đã thử 'ORDER BY reader_name, time_change DESC, pk'. Nó không hoạt động cho MariaDB. Nhưng tôi đoán nó sẽ làm cho mã MySQL ổn định hơn vì lý do bạn đã nói. Đã bình chọn câu trả lời của bạn. –

4

Việc thực hiện truy vấn được phép bỏ qua số ORDER BY trong FROM (SELECT ...). Đây có lẽ là lý do thực sự cho sự khác biệt mà bạn đang thấy. (Tôi không nghĩ câu trả lời của Gordon có liên quan.)

Vấn đề được thảo luận ở đây (4 năm trước): https://mariadb.com/kb/en/mariadb/group-by-trick-has-been-optimized-away/; có một giải pháp, thông qua một thiết lập.

Một số giải pháp khác có tại đây: http://mysql.rjweb.org/doc.php/groupwise_max; chúng được thiết kế hiệu quả.

Một giải pháp khác có thể là thêm một số không thật LIMIT với số lượng lớn trên truy vấn phụ.

1

Cách cổ điển của việc lựa chọn N hàng đầu cho mỗi nhóm sử dụng các cấu trúc ngôn ngữ SQL chuẩn là sử dụng ROW_NUMBER:

SELECT 
    T.* 
FROM 
    (
     SELECT * 
      ,ROW_NUMBER() OVER (PARTITION BY reader_name ORDER BY time_change DESC) AS rn 
     FROM tmp 
    ) AS T 
WHERE T.rn <= 3 
ORDER BY reader_name, time_change DESC; 

Truy vấn này nên làm việc trong tất cả các DBMS hỗ trợ ROW_NUMBER. MySQL không hỗ trợ nó, vì vậy mọi người phải sử dụng các thủ thuật mong manh với các biến cụ thể cho MySQL.

Window functions lần đầu tiên được giới thiệu trong MariaDB 10.2.0. MariaDB có nhiều tự do hơn trong việc tối ưu hóa các truy vấn và thủ thuật MySQL này với các biến không đáng tin cậy nữa.

Vì vậy, trả lời câu hỏi của bạn, có, sử dụng chức năng cửa sổ trong MariaDB sẽ là một ý tưởng hay.


Một phương pháp phổ biến của việc lựa chọn N hàng đầu cho mỗi nhóm được sử dụng LATERAL tham gia, đó là tốt hơn so với ROW_NUMBER khi số lượng các nhóm nhỏ và số lượng hàng trong một bảng lớn và bạn có một chỉ số thích hợp và một giây bảng với danh sách các nhóm. Tôi không biết MariaDB có hỗ trợ tham gia LATERAL hay không. Có vẻ như không.

1

(My) SQL không bắt buộc phải giữ thứ tự kết quả truy vấn phụ của bạn. Bạn phải đặt resultset trong truy vấn trên, nhưng trong trường hợp của bạn, bạn thực sự có thể thoát khỏi subquery anyway:

SELECT mac_addr, reader_name, value, time_change 
FROM (
    SELECT t1.*, 
      IF(@rn = reader_name, @rowno := @rowno + 1, @rowno := 1) AS rowno, 
      @rn := reader_name 
    FROM tmp t1, (SELECT @rn := null, @rowno := 0) t2 
    ORDER BY reader_name, time_change DESC 
) t 
WHERE rowno <= 3; 

Chỉ cần cho đầy đủ: Hành vi này là cụ thể cho việc sử dụng các biến, và kể từ khi Kết quả thực sự không được định nghĩa trong tiêu chuẩn sql, nó có thể thay đổi một ngày (như sự tối ưu gây rắc rối cho bạn ngay bây giờ), nhưng điều này rất có thể sẽ không xảy ra, nếu có, cho đến khi các chức năng cửa sổ được hỗ trợ đầy đủ, vì vậy bạn có thể bỏ qua chi tiết này. Điều tương tự có thể đúng với các cách khác buộc một đơn đặt hàng, như thêm limit 999999999 bên trong truy vấn bên trong của bạn như Rick gợi ý, mặc dù tôi có thể nghĩ ra một số đường dẫn tối ưu hóa (chưa được triển khai) có thể dẫn đến một thứ tự không xác định.

Các vấn đề liên quan