ROW_NUMBER () trong MySQL


281

Có một cách hay trong MySQL để sao chép chức năng SQL Server ROW_NUMBER()không?

Ví dụ:

SELECT 
    col1, col2, 
    ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1

Sau đó, tôi có thể, ví dụ, thêm một điều kiện để giới hạn intRowở 1 để có được một hàng với mức cao nhất col3cho mỗi (col1, col2)cặp.


9
Tagged với greatest-n-per-groupđể hướng dẫn bạn cho các câu hỏi tương tự.
Bill Karwin

1
Sql-ServerThẻ đã bị xóa vì đây là mục được bình chọn cao nhất trong tìm kiếm thẻ kết hợp nhưng không thực sự phù hợp với SQL Server.
Martin Smith

để biết chức năng số hàng mys mys
AdrianBR

Đối với MySQL, các giải pháp hiệu quả duy nhất được thảo luận trong mysql.rjweb.org/doc.php/groupwise_max . Nhiều giải pháp công khai yêu cầu quét toàn bộ bảng (hoặc tệ hơn).
Rick James

2
Giải pháp tự quảng cáo không biết xấu hổ cho chức năng phân tích thực ROW_NUMBER, RANK, DESNSE_RANK trong MySQL
Kenneth Xu

Câu trả lời:


102

Tôi muốn hàng có col3 cao nhất duy nhất cho mỗi cặp (col1, col2).

Đó là tối đa theo nhóm , một trong những câu hỏi SQL thường được hỏi nhất (vì có vẻ như nó sẽ dễ, nhưng thực ra nó không phải vậy).

Tôi thường đầy đặn cho một người tự tham gia null:

SELECT t0.col3
FROM table AS t0
LEFT JOIN table AS t1 ON t0.col1=t1.col1 AND t0.col2=t1.col2 AND t1.col3>t0.col3
WHERE t1.col1 IS NULL;

Chọn các hàng trong bảng mà không có hàng nào khác phù hợp với col1, col2 có col3 cao hơn. (Bạn sẽ nhận thấy điều này và hầu hết các giải pháp tối đa theo nhóm khác sẽ trả về nhiều hàng nếu nhiều hơn một hàng có cùng col1, col2, col3. Nếu đó là vấn đề bạn có thể cần xử lý hậu kỳ.)


2
Nhưng nếu có hai giá trị tối đa của col3 cho một cặp (col1, col2) thì sao? Bạn sẽ kết thúc với hai hàng.
Paul

@Paul: vâng! Chỉ cần thêm một lưu ý về điều đó trong câu trả lời một tic trước đây. Bạn thường có thể dễ dàng bỏ các hàng thừa không mong muốn trong lớp ứng dụng sau đó trên một số cơ sở ngẫu nhiên, nhưng nếu bạn có rất nhiều hàng với cùng một col3 thì điều đó có thể gây ra vấn đề.
bobince

1
bobince, giải pháp đã trở nên khá phổ biến ở đây trên SO, nhưng tôi có một câu hỏi. Giải pháp về cơ bản giống như khi ai đó cố gắng tìm id lớn nhất với truy vấn sau: SELECT t1.id FROM test t1 LEFT JOIN test t2 ON t1.id>t2.id WHERE t2.id IS NULL;Không yêu cầu n*n/2 + n/2so sánh IS NULL để tìm hàng đơn sao? Có xảy ra bất kỳ tối ưu hóa tôi không thấy? Tôi đã cố gắng đặt câu hỏi tương tự với Bill trong một chủ đề khác nhưng anh ta dường như đã bỏ qua nó.
newtover

2
@Paul - Để giải quyết trường hợp tồn tại nhiều hàng khớp với mức tối đa của mỗi nhóm và bạn muốn lấy chỉ một, bạn luôn có thể thêm khóa chính trong logic mệnh đề ON để phá vỡ liên kết ... CHỌN t0.col3 TỪ bảng NHƯ bảng t0 TRÁI THƯỞNG NHƯ T1.col1 = t1.col1 VÀ t0.col2 = t1.col2 VÀ (t1.col3, t1.pk)> (t0.col3, t0.pk) WHERE t1.col1 LÀ NULL;
Jon Armstrong - Xgc

2
Điều này sẽ dễ đọc hơn vìSELECT t0.col3 FROM table AS t0 WHERE NOT EXISTS (select 1 from table AS t1 ON t0.col1=t1.col1 AND t0.col2=t1.col2 AND t1.col3>t0.col3)
wrschneider

204

Không có chức năng xếp hạng trong MySQL. Cách gần nhất bạn có thể nhận được là sử dụng một biến:

SELECT t.*, 
       @rownum := @rownum + 1 AS rank
  FROM YOUR_TABLE t, 
       (SELECT @rownum := 0) r

Vì vậy, làm thế nào mà làm việc trong trường hợp của tôi? Tôi cần hai biến, một biến cho mỗi col1 và col2? Col2 sẽ cần thiết lập lại bằng cách nào đó khi col1 thay đổi ..?

Đúng. Nếu đó là Oracle, bạn có thể sử dụng hàm LEAD để đạt đỉnh ở giá trị tiếp theo. Rất may, Quassnoi bao quát logic cho những gì bạn cần triển khai trong MySQL .


1
Hmm .... vậy làm thế nào nó sẽ làm việc trong trường hợp của tôi? Tôi cần hai biến, một biến cho mỗi col1 và col2? Col2 sẽ cần thiết lập lại bằng cách nào đó khi col1 thay đổi ..?
Paul

Cảm ơn ... như tôi đã nói ở trên, câu trả lời này cũng được chấp nhận như nhau, nhưng tôi chỉ có thể đánh dấu một :-)
Paul

9
Việc chỉ định và đọc từ các biến do người dùng xác định trong cùng một câu lệnh là không đáng tin cậy. đây là tài liệu ở đây: dev.mysql.com/doc/refman/5.0/en/user-variabled.html : "Theo quy tắc chung, bạn không bao giờ nên gán giá trị cho biến người dùng và đọc giá trị trong cùng một câu lệnh. Bạn có thể nhận được kết quả mà bạn mong đợi, nhưng điều này không được đảm bảo. Thứ tự đánh giá các biểu thức liên quan đến biến người dùng không được xác định và có thể thay đổi dựa trên các yếu tố có trong một tuyên bố nhất định. "
Roland Bouman

1
@Roland: Tôi chỉ thử nghiệm trên các bộ dữ liệu nhỏ, không có vấn đề gì. Quá tệ, MySQL vẫn chưa giải quyết được chức năng - yêu cầu đã có từ năm 2008
OMG Ponies

2
Đây dường như là hành vi không xác định như ghi chú Roland. ví dụ: điều này mang lại kết quả hoàn toàn không chính xác cho một bảng tôi đã thử:SELECT @row_num:=@row_num+1 AS row_number, t.id FROM (SELECT * FROM table1 WHERE col = 264 ORDER BY id) t, (SELECT @row_num:=0) var;
jberryman

81

Tôi luôn luôn theo mô hình này. Đưa ra bảng này:

+------+------+
|    i |    j |
+------+------+
|    1 |   11 |
|    1 |   12 |
|    1 |   13 |
|    2 |   21 |
|    2 |   22 |
|    2 |   23 |
|    3 |   31 |
|    3 |   32 |
|    3 |   33 |
|    4 |   14 |
+------+------+

Bạn có thể nhận được kết quả này:

+------+------+------------+
|    i |    j | row_number |
+------+------+------------+
|    1 |   11 |          1 |
|    1 |   12 |          2 |
|    1 |   13 |          3 |
|    2 |   21 |          1 |
|    2 |   22 |          2 |
|    2 |   23 |          3 |
|    3 |   31 |          1 |
|    3 |   32 |          2 |
|    3 |   33 |          3 |
|    4 |   14 |          1 |
+------+------+------------+

Bằng cách chạy truy vấn này, không cần bất kỳ biến nào được xác định:

SELECT a.i, a.j, count(*) as row_number FROM test a
JOIN test b ON a.i = b.i AND a.j >= b.j
GROUP BY a.i, a.j

Mong rằng sẽ giúp!


1
nếu các cột là VARCHAR hoặc CHAR, làm thế nào bạn có thể xử lý điều đó với cấu trúc này?
Tushar

3
Bạn thật tuyệt vời, tôi đang tìm kiếm chính xác cho điều này
luckykrrish

Chỉ cần đưa ra câu trả lời này bằng cách sử dụng logic của bạn cho row_number. Cảm ơn.
Utsav

@Tushar các nhà khai thác <, >, <=, >=xử lý CHAR và các kiểu dữ liệu VARCHAR trên thứ tự chữ cái; Tôi mong đợi, chính xác là những gì bạn đang tìm kiếm.
alex

1
@AlmazVildanov bạn sẽ có thể sử dụng truy vấn này một cách đơn giản như một truy vấn con để lọc ra row_numbers <= 2 và cảm ơn rất nhiều vì câu trả lời này Hầu hết, nó hoàn hảo!
Zax

61
SELECT 
    @i:=@i+1 AS iterator, 
    t.*
FROM 
    tablename AS t,
    (SELECT @i:=0) AS foo

1
Đầu tiên: = dường như bị thiếu trong câu trả lời của @OMG Ponies. Cảm ơn bạn đã đăng bài này Peter Johnson.
sholsinger

Tôi đoán (SELECT @i: = 0) NHƯ foo nên là bảng đầu tiên trong câu lệnh TỪ, đặc biệt nếu các bảng khác sử dụng các lựa chọn phụ
andig

Tại sao bạn thậm chí cần '.. như foo'?
Tom Chiverton

@TomChiverton Nếu thiếu, bạn nhận được: "Mã lỗi: 1248. Mỗi bảng dẫn xuất phải có bí danh riêng"
ExStackChanger

1
Việc gán thứ hạng ở đây hoàn toàn không được xác định và điều này thậm chí không trả lời được câu hỏi
jberryman

27

Kiểm tra bài viết này, nó cho thấy cách bắt chước SQL ROW_NUMBER () với một phân vùng trong MySQL. Tôi đã gặp phải tình huống tương tự trong triển khai WordPress. Tôi cần ROW_NUMBER () và nó không có ở đó.

http://www.explodybits.com/2011/11/mysql-row-number/

Ví dụ trong bài viết đang sử dụng một phân vùng duy nhất theo trường. Để phân vùng theo các trường bổ sung, bạn có thể làm một cái gì đó như thế này:

  SELECT  @row_num := IF(@prev_value=concat_ws('',t.col1,t.col2),@row_num+1,1) AS RowNumber
         ,t.col1 
         ,t.col2
         ,t.Col3
         ,t.col4
         ,@prev_value := concat_ws('',t.col1,t.col2)
    FROM table1 t,
         (SELECT @row_num := 1) x,
         (SELECT @prev_value := '') y
   ORDER BY t.col1,t.col2,t.col3,t.col4 

Sử dụng concat_ws xử lý null. Tôi đã thử nghiệm điều này với 3 trường bằng cách sử dụng một int, date và varchar. Hi vọng điêu nay co ich. Kiểm tra bài viết vì nó phá vỡ truy vấn này và giải thích nó.


1
Tuyệt vời. Điều này thực sự làm phân vùng. Rất tiện dụng
Stuart Watt

1
So sánh với tự tham gia, điều này hiệu quả hơn nhiều, nhưng có một vấn đề với logic, thứ tự phải xảy ra trước khi tính toán row_num, concat cũng không cần thiết. `` `CHỌN @row_num: = IF (@ trước_col1 = t.col1 VÀ @ trước_col2 = t.col2), @ row_num + 1, 1) NHƯ RowNumber, t.col1, t.col2, t.col3, t.col4 , @ trước_col1: = t.col1, @ trước_col2: = t.col2 TỪ (CHỌN * TỪ bảng1 ĐẶT HÀNG B colNG col1, col2, col3) t, (CHỌN @row_num: = 1, @ trước_col1: = '', @ trước_col2: = '') var `` `
Kenneth Xu

Nếu bạn cần tu đặt điều này vào một truy vấn con, sau đó thêm limit 18446744073709551615vào order bymệnh đề lực .
xmedeko

concat_wsvới chuỗi rỗng ''là nguy hiểm : concat_ws('',12,3) = concat_ws('',1,23). Tốt hơn là sử dụng một số dải phân cách '_'hoặc sử dụng giải pháp @Kenneth Xu.
xmedeko

liên kết của op đã chết; lưu trữ liên kết tại đây
user2426679

25

Từ MySQL 8.0.0và trên, bạn có thể sử dụng các chức năng cửa sổ.

1.4 Có gì mới trong MySQL 8.0 :

Chức năng cửa sổ.

MySQL hiện hỗ trợ các hàm cửa sổ, cho mỗi hàng từ một truy vấn, thực hiện phép tính bằng các hàng liên quan đến hàng đó. Chúng bao gồm các hàm như RANK (), LAG () và NTILE (). Ngoài ra, một số hàm tổng hợp hiện có có thể được sử dụng làm hàm cửa sổ; ví dụ: SUM () và AVG ().

ROW_NUMBER () over_clided :

Trả về số lượng hàng hiện tại trong phân vùng của nó. Hàng số nằm trong khoảng từ 1 đến số hàng phân vùng.

ORDER BY ảnh hưởng đến thứ tự các hàng được đánh số. Không có thứ tự theo thứ tự, đánh số hàng là không xác định.

Bản giới thiệu:

CREATE TABLE Table1(
  id INT AUTO_INCREMENT PRIMARY KEY, col1 INT,col2 INT, col3 TEXT);

INSERT INTO Table1(col1, col2, col3)
VALUES (1,1,'a'),(1,1,'b'),(1,1,'c'),
       (2,1,'x'),(2,1,'y'),(2,2,'z');

SELECT 
    col1, col2,col3,
    ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1;

Trình diễn DBFiddle


1
thở dài ... cuối cùng!
Được sử dụng_By_Al sẵn

15

Tôi cũng sẽ bỏ phiếu cho giải pháp của Mosty Mostacho với sửa đổi nhỏ cho mã truy vấn của anh ấy:

SELECT a.i, a.j, (
    SELECT count(*) from test b where a.j >= b.j AND a.i = b.i
) AS row_number FROM test a

Mà sẽ cho kết quả tương tự:

+------+------+------------+
|    i |    j | row_number |
+------+------+------------+
|    1 |   11 |          1 |
|    1 |   12 |          2 |
|    1 |   13 |          3 |
|    2 |   21 |          1 |
|    2 |   22 |          2 |
|    2 |   23 |          3 |
|    3 |   31 |          1 |
|    3 |   32 |          2 |
|    3 |   33 |          3 |
|    4 |   14 |          1 |
+------+------+------------+

cho bảng:

+------+------+
|    i |    j |
+------+------+
|    1 |   11 |
|    1 |   12 |
|    1 |   13 |
|    2 |   21 |
|    2 |   22 |
|    2 |   23 |
|    3 |   31 |
|    3 |   32 |
|    3 |   33 |
|    4 |   14 |
+------+------+

Với sự khác biệt duy nhất là truy vấn không sử dụng THAM GIA và NHÓM THEO, thay vào đó, dựa vào lựa chọn lồng nhau.


Đây có phải là tốt hơn? Cả hai dường như là bậc hai, nhưng tôi không chắc làm thế nào để diễn giải đầu ra GIẢI THÍCH
jberryman

Trong thực tế, các lựa chọn lồng nhau được biết là không được tối ưu hóa tốt trong MySQL, do đó, anwser này chỉ để trình diễn một kỹ thuật truy vấn. Các ví dụ dựa trên biến trên hoạt động tốt hơn cho hầu hết các trường hợp thực tế, tôi cho rằng.
abcdn

1
Tôi không tin bất kỳ câu trả lời dựa trên biến nào đang thực sự sử dụng hành vi được xác định ...
jberryman

Tôi xin lỗi, tôi không chắc tôi hiểu ý của bạn về "hành vi được xác định". Bạn có nghĩa là nó không làm việc cho bạn, hoặc bạn chỉ lo lắng rằng nó không được ghi chép lại?
abcdn

1
"Hành vi không xác định" có nghĩa là nó không được ghi nhận để làm việc và / hoặc được ghi lại để không được đảm bảo để làm việc. Xem tài liệu báo giá & liên kết trong các ý kiến ​​trên trang này. Nó có thể trả về những gì một người (không chắc chắn) muốn / đoán / đưa ra giả thuyết / tưởng tượng. Đối với các phiên bản nhất định của việc triển khai một số biểu thức truy vấn nhất định bằng cách sử dụng các biến tăng & sử dụng CASE đã được các lập trình viên tại Percona hiển thị để hoạt động bằng cách xem mã. Điều đó có thể thay đổi với bất kỳ bản phát hành.
philipxy

12

Tôi sẽ xác định một chức năng:

delimiter $$
DROP FUNCTION IF EXISTS `getFakeId`$$
CREATE FUNCTION `getFakeId`() RETURNS int(11)
    DETERMINISTIC
begin
return if(@fakeId, @fakeId:=@fakeId+1, @fakeId:=1);
end$$

sau đó tôi có thể làm:

select getFakeId() as id, t.* from table t, (select @fakeId:=0) as t2;

Bây giờ bạn không có truy vấn con mà bạn không thể có trong lượt xem.


Hoạt động với một giới hạn: nếu bạn thực hiện truy vấn nhiều lần, bạn sẽ nhận được các giả mạo ngày càng tăng cho cùng một tập kết quả
Stephan Richter

bạn có thể gửi tập @fakeId = 0; mỗi lần bạn muốn chạy truy vấn, không tối ưu nhưng hoạt động
jmpeace

Một vấn đề thực sự kỳ lạ xảy ra nếu bạn loại bỏ DETERMINISTIC. Sau đó, fakeId không chính xác khi sử dụng thứ tự bởi. Tại sao lại thế này?
Chris Muench

8

truy vấn row_number trong mysql

set @row_number=0;
select (@row_number := @row_number +1) as num,id,name from sbs

Điều này có thể được sử dụng trên các truy vấn CẬP NHẬT? Tôi đang thử nhưng tôi gặp lỗi "dữ liệu bị cắt cho cột ...".
Diego

1
Nếu bất cứ ai quan tâm đến việc sử dụng nó trên CẬP NHẬT, nó phải được sử dụng như một truy vấn phụ để hoạt động. CẬP NHẬT <bảng> SET <trường> = (CHỌN \ @row_number: = \ @row_number +1) ĐẶT HÀNG THEO <cột đơn hàng của bạn>; Cột thứ tự xác định thứ tự giá trị của các hàng.
Diego

8

Không có Chức năng như rownum, row_num()trong MySQL nhưng con đường xung quanh cũng giống như dưới đây:

select 
      @s:=@s+1 serial_no, 
      tbl.* 
from my_table tbl, (select @s:=0) as s;

4

Giải pháp tôi thấy hiệu quả nhất là sử dụng truy vấn con như thế này:

SELECT 
    col1, col2, 
    (
        SELECT COUNT(*) 
        FROM Table1
        WHERE col1 = t1.col1
        AND col2 = t1.col2
        AND col3 > t1.col3
    ) AS intRow
FROM Table1 t1

Các cột THAM GIA B BYNG chỉ được so sánh với '=' và được phân tách bằng AND. Các cột ORDER BY sẽ được so sánh với '<' hoặc '>' và được phân tách bằng OR.

Tôi thấy điều này rất linh hoạt, ngay cả khi nó hơi tốn kém.


4

Các chức năng của rownumber không thể được bắt chước. Bạn có thể nhận được kết quả mà bạn mong đợi, nhưng rất có thể bạn sẽ thất vọng ở một giai đoạn nào đó. Dưới đây là những gì tài liệu mysql nói:

Đối với các báo cáo khác, chẳng hạn như CHỌN, bạn có thể nhận được kết quả mà bạn mong đợi, nhưng điều này không được đảm bảo. Trong tuyên bố sau, bạn có thể nghĩ rằng MySQL sẽ đánh giá @a trước và sau đó thực hiện một nhiệm vụ thứ hai: SELECT @a, @a: = @ a + 1, ...; Tuy nhiên, thứ tự đánh giá cho các biểu thức liên quan đến các biến người dùng là không xác định.

Trân trọng, Georgi.


Tôi không làm theo. Làm thế nào "@i: = @i + 1 là vị trí" không phải là sự thay thế trực tiếp cho "ROW_NUMBER () hơn (thứ tự theo tổng (điểm) desc) như vị trí"?
Tom Chiverton

1
@TomChiverton Vì hành vi của nó không được xác định, vì hướng dẫn sử dụng nói ngay tại đó.
philipxy

4

MariaDB 10.2 đang triển khai "Các hàm cửa sổ", bao gồm RANK (), ROW_NUMBER () và một số thứ khác:

https://mariadb.com/kb/en/mariadb/window-fifts/

Dựa trên cuộc nói chuyện tại Percona Live trong tháng này, chúng được tối ưu hóa hợp lý.

Cú pháp giống hệt với mã trong Câu hỏi.


2

Tôi không thấy bất kỳ câu trả lời đơn giản nào bao gồm phần "PHẦN THAM GIA" vì vậy đây là của tôi:

SELECT
    *
FROM (
    select
        CASE WHEN @partitionBy_1 = l THEN @row_number:=@row_number+1 ELSE @row_number:=1 END AS i
        , @partitionBy_1:=l AS p
        , t.*
    from (
        select @row_number:=0,@partitionBy_1:=null
    ) as x
    cross join (
        select 1 as n, 'a' as l
        union all
        select 1 as n, 'b' as l    
        union all
        select 2 as n, 'b' as l    
        union all
        select 2 as n, 'a' as l
        union all
        select 3 as n, 'a' as l    
        union all    
        select 3 as n, 'b' as l    
    ) as t
    ORDER BY l, n
) AS X
where i > 1
  • Mệnh đề ORDER BY phải phản ánh nhu cầu ROW_NUMBER của bạn. Do đó, đã có một giới hạn rõ ràng: bạn không thể có một số "mô phỏng" ROW_NUMBER của biểu mẫu này cùng một lúc.
  • Thứ tự của "cột tính toán" có vấn đề . Nếu bạn có mysql tính toán các cột đó theo thứ tự khác, nó có thể không hoạt động.
  • Trong ví dụ đơn giản này, tôi chỉ đặt một nhưng bạn có thể có một số phần "THAM GIA B" NG "

        CASE WHEN @partitionBy_1 = part1 AND @partitionBy_2 = part2 [...] THEN @row_number:=@row_number+1 ELSE @row_number:=1 END AS i
        , @partitionBy_1:=part1 AS P1
        , @partitionBy_2:=part2 AS P2
        [...] 
    FROM (
        SELECT @row_number:=0,@partitionBy_1:=null,@partitionBy_2:=null[...]
    ) as x

1

Hơi muộn một chút nhưng cũng có thể giúp một người tìm kiếm câu trả lời ...

Giữa các hàng / ví dụ row_number - truy vấn đệ quy có thể được sử dụng trong bất kỳ SQL nào:

WITH data(row_num, some_val) AS 
(
 SELECT 1 row_num, 1 some_val FROM any_table --dual in Oracle
  UNION ALL
 SELECT row_num+1, some_val+row_num FROM data WHERE row_num < 20 -- any number
)
SELECT * FROM data
 WHERE row_num BETWEEN 5 AND 10
/

ROW_NUM    SOME_VAL
-------------------
5           11
6           16
7           22
8           29
9           37
10          46

2
Xin lỗi nhưng theo tôi biết MySQL không hỗ trợ các biểu thức bảng chung .
Álvaro González

hiện tại ... @ ÁlvaroGonzález MySQL 8 chỉ hỗ trợ các chức năng CTE và cửa sổ, vì vậy câu trả lời này không thực sự có ý nghĩa khi sử dụng trong các phiên bản MySQL cũ hơn ..
Raymond Nijland

1

Điều này cho phép các chức năng tương tự mà ROW_NUMBER () VÀ PHẦN THAM GIA cung cấp để đạt được trong MySQL

SELECT  @row_num := IF(@prev_value=GENDER,@row_num+1,1) AS RowNumber
       FirstName, 
       Age,
       Gender,
       @prev_value := GENDER
  FROM Person,
      (SELECT @row_num := 1) x,
      (SELECT @prev_value := '') y
  ORDER BY Gender, Age DESC

1

Cũng hơi muộn nhưng hôm nay tôi có cùng nhu cầu nên tôi đã tìm kiếm trên Google và cuối cùng, một cách tiếp cận chung đơn giản được tìm thấy ở đây trong bài viết của Pinal Dave http://blog.sqlauthority.com/2014/03/09/mysql-reset-row -Số-cho-mỗi-nhóm-phân vùng-theo-hàng-số /

Tôi muốn tập trung vào câu hỏi ban đầu của Paul (đó cũng là vấn đề của tôi) vì vậy tôi tóm tắt giải pháp của mình như một ví dụ hoạt động.

Beacuse chúng tôi muốn phân vùng trên hai cột Tôi sẽ tạo một biến SET trong quá trình lặp để xác định nếu một nhóm mới được bắt đầu.

SELECT col1, col2, col3 FROM (
  SELECT col1, col2, col3,
         @n := CASE WHEN @v = MAKE_SET(3, col1, col2)
                    THEN @n + 1 -- if we are in the same group
                    ELSE 1 -- next group starts so we reset the counter
                END AS row_number,
         @v := MAKE_SET(3, col1, col2) -- we store the current value for next iteration
    FROM Table1, (SELECT @n := 0, @v := NULL) r -- helper table for iteration with startup values
   ORDER BY col1, col2, col3 DESC -- because we want the row with maximum value
) x WHERE row_number = 1 -- and here we select exactly the wanted row from each group

3 có nghĩa là ở tham số đầu tiên của MAKE_SET mà tôi muốn cả hai giá trị trong SET (3 = 1 | 2). Tất nhiên, nếu chúng ta không có hai hoặc nhiều cột xây dựng các nhóm, chúng ta có thể loại bỏ thao tác MAKE_SET. Việc xây dựng là hoàn toàn giống nhau. Điều này làm việc cho tôi theo yêu cầu. Rất cám ơn Pinal Dave vì đã trình diễn rõ ràng.


1
Lưu ý rằng ORDER BYtrong một truy vấn con có thể bị bỏ qua (xem mariadb.com/kb/en/mariadb/iêu ). Giải pháp được đề xuất cho điều đó là thêm LIMIT 18446744073709551615vào truy vấn con, điều này buộc một loại. Tuy nhiên, điều này có thể gây ra các vấn đề về hiệu suất và không hợp lệ cho các bảng khổng lồ thực sự đáng sợ :)
pnomolos

1

Đây cũng có thể là một giải pháp:

SET @row_number = 0;

SELECT 
    (@row_number:=@row_number + 1) AS num, firstName, lastName
FROM
    employees

Nó không thực hiện bất kỳ phân vùng nào, và nó không khác biệt đáng kể với câu trả lời được trích dẫn cao hơn
Caius Jard

1

MySQL đã hỗ trợ ROW_NUMBER () kể từ phiên bản 8.0 trở lên .

Nếu bạn sử dụng MySQL 8.0 trở lên, hãy kiểm tra hàm ROW_NUMBER (). Mặt khác, bạn đã mô phỏng hàm ROW_NUMBER ().

Row_number () là một hàm xếp hạng trả về số thứ tự của một hàng, bắt đầu từ 1 cho hàng đầu tiên.

cho phiên bản cũ hơn,

SELECT t.*, 
       @rowid := @rowid + 1 AS ROWID
  FROM TABLE t, 
       (SELECT @rowid := 0) dummy;

1

Quan trọng: Vui lòng xem xét nâng cấp lên MySQL 8+ và sử dụng hàm ROW_NUMBER () được xác định và ghi lại và bỏ các bản hack cũ gắn với một phiên bản MySQL cổ có giới hạn tính năng

Bây giờ đây là một trong những hack đó:

Các câu trả lời ở đây sử dụng hầu hết các biến trong truy vấn / tất cả dường như bỏ qua thực tế là tài liệu nói (paraph cụm từ):

Đừng dựa vào các mục trong danh sách CHỌN được đánh giá theo thứ tự từ trên xuống dưới. Không gán các biến trong một mục CHỌN và sử dụng chúng trong một mục khác

Như vậy, có nguy cơ họ sẽ đưa ra câu trả lời sai, bởi vì họ thường làm một

select
  (row number variable that uses partition variable),
  (assign partition variable)

Nếu chúng được đánh giá từ dưới lên, số hàng sẽ ngừng hoạt động (không có phân vùng)

Vì vậy, chúng ta cần phải sử dụng một cái gì đó với một trật tự thực hiện được đảm bảo. Nhập TRƯỜNG HỢP KHI:

SELECT
  t.*, 
  @r := CASE 
    WHEN col = @prevcol THEN @r + 1 
    WHEN (@prevcol := col) = null THEN null
    ELSE 1 END AS rn
FROM
  t, 
  (SELECT @r := 0, @prevcol := null) x
ORDER BY col

Như phác thảo ld, thứ tự gán của thuận là rất quan trọng - phải so sánh với giá trị của hàng hiện tại trước khi chúng ta gán giá trị từ hàng hiện tại (nếu không nó sẽ là giá trị col của hàng hiện tại, không phải giá trị col của hàng trước) .

Đây là cách điều này phù hợp với nhau:

  • KHI đầu tiên được đánh giá. Nếu col của hàng này giống với col của hàng trước thì @r được tăng lên và được trả về từ CASE. Giá trị led trả về này được lưu trữ trong @r. Đây là một tính năng của MySQL mà phép gán trả về giá trị mới của những gì được gán vào @r vào các hàng kết quả.

  • Đối với hàng đầu tiên trên tập kết quả, @prevcol là null (nó được khởi tạo thành null trong truy vấn con) vì vậy vị từ này là sai. Vị từ đầu tiên này cũng trả về false mỗi lần thay đổi col (hàng hiện tại khác với hàng trước). Điều này gây ra KHI thứ hai được đánh giá.

  • Vị từ thứ hai KHI luôn luôn sai và nó tồn tại hoàn toàn để gán giá trị mới cho @prevcol. Vì col của hàng này khác với col của hàng trước (chúng tôi biết điều này bởi vì nếu nó giống nhau, WHEN đầu tiên sẽ được sử dụng), chúng tôi phải gán giá trị mới để giữ cho thử nghiệm lần sau. Bởi vì phép gán được thực hiện và sau đó kết quả của phép gán được so sánh với null và mọi thứ tương đương với null là sai, vị từ này luôn luôn là sai. Nhưng ít nhất là đánh giá nó đã làm công việc giữ giá trị col từ hàng này, vì vậy nó có thể được đánh giá theo giá trị col của hàng tiếp theo

  • Bởi vì WHEN thứ hai là sai, điều đó có nghĩa là trong trường hợp cột mà chúng ta phân vùng theo (col) đã thay đổi, thì ELSE cung cấp giá trị mới cho @r, khởi động lại việc đánh số từ 1

Chúng tôi nhận được một tình huống này:

SELECT
  t.*, 
  ROW_NUMBER() OVER(PARTITION BY pcol1, pcol2, ... pcolX ORDER BY ocol1, ocol2, ... ocolX) rn
FROM
  t

Có dạng chung:

SELECT
  t.*, 
  @r := CASE 
    WHEN col1 = @pcol1 AND col2 = @pcol2 AND ... AND colX = @pcolX THEN @r + 1 
    WHEN (@pcol1 := pcol1) = null OR (@pcol2 := col2) = null OR ... OR (@pcolX := colX) = null THEN null
    ELSE 1 
  END AS rn
FROM
  t, 
  (SELECT @r := 0, @pcol1 := null, @pcol2 := null, ..., @pcolX := null) x
ORDER BY pcol1, pcol2, ..., pcolX, ocol1, ocol2, ..., ocolX

Chú thích:

  • P trong pcol có nghĩa là "phân vùng", o trong atio có nghĩa là "trật tự" - ở dạng chung tôi đã bỏ "tiền tố" từ tên biến để giảm sự lộn xộn thị giác

  • Các dấu ngoặc xung quanh (@pcolX := colX) = nulllà quan trọng. Nếu không có họ, bạn sẽ gán null cho @pcolX và mọi thứ sẽ ngừng hoạt động

  • Đó là một sự thỏa hiệp rằng tập kết quả cũng phải được sắp xếp theo các cột phân vùng, cho cột trước đó so với giải quyết. Do đó, bạn không thể yêu cầu số thứ tự của mình theo một cột nhưng tập kết quả của bạn được sắp xếp theo một cột khác Bạn có thể giải quyết vấn đề này bằng các truy vấn con nhưng tôi tin rằng các tài liệu cũng nói rằng việc đặt hàng truy vấn phụ có thể bị bỏ qua trừ khi LIMIT được sử dụng và điều này có thể ảnh hưởng hiệu suất

  • Tôi đã không đào sâu vào nó ngoài việc kiểm tra rằng phương thức hoạt động, nhưng nếu có rủi ro thì các biến vị ngữ trong lần thứ hai KHI sẽ được tối ưu hóa (mọi thứ so với null là null / false, vậy tại sao phải chạy bài tập) và không được thực thi , nó cũng dừng lại. Điều này dường như không xảy ra theo kinh nghiệm của tôi nhưng tôi sẵn sàng chấp nhận ý kiến ​​và đề xuất giải pháp nếu nó có thể xảy ra một cách hợp lý

  • Có thể là khôn ngoan khi truyền các null tạo @pcolX cho các loại cột thực tế của bạn, trong truy vấn con tạo các biến @pcolX, viz: select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)


Không có lời biện minh cho điều này. Cũng giống như các câu trả lời khác gán cho & đọc từ cùng một biến.
philipxy

Bạn có thể cung cấp chi tiết hơn phil?
Caius Jard

Xem ý kiến ​​khác của tôi trên trang này. Googling 'site: stackoverflow.com Biến "mysipxy" (đặt OR gán hoặc gán OR OR) đọc': Một câu trả lời của tôimột báo cáo lỗi được liên kết trong một nhận xét của tôi tại câu hỏi này khi câu trả lời được chấp nhận trích dẫn ngay lập tức trong tuyên bố, bạn có thể làm điều gì đó mâu thuẫn với nó. Đọc các biến re thủ công & gán lại.
philipxy


Tôi hiểu mối quan tâm của bạn
Caius Jard

0

Đây không phải là giải pháp mạnh mẽ nhất - nhưng nếu bạn chỉ muốn tạo thứ hạng được phân vùng trên một trường chỉ có một vài giá trị khác nhau, thì có thể không sử dụng một số trường hợp khi logic có nhiều biến như bạn yêu cầu.

Một cái gì đó như thế này đã làm việc cho tôi trong quá khứ:

SELECT t.*, 
   CASE WHEN <partition_field> = @rownum1 := @rownum1 + 1 
     WHEN <partition_field> = @rownum2 := @rownum2 + 1 
     ...
     END AS rank
FROM YOUR_TABLE t, 
   (SELECT @rownum1 := 0) r1, (SELECT @rownum2 := 0) r2
ORDER BY <rank_order_by_field>
;

Hy vọng rằng có ý nghĩa / giúp đỡ!


-1

Công việc này hoàn hảo đối với tôi để tạo RowNumber khi chúng tôi có nhiều hơn một cột. Trong trường hợp này hai cột.

SELECT @row_num := IF(@prev_value= concat(`Fk_Business_Unit_Code`,`NetIQ_Job_Code`), @row_num+1, 1) AS RowNumber, 
    `Fk_Business_Unit_Code`,   
    `NetIQ_Job_Code`,  
    `Supervisor_Name`,  
    @prev_value := concat(`Fk_Business_Unit_Code`,`NetIQ_Job_Code`)  
FROM (SELECT DISTINCT `Fk_Business_Unit_Code`,`NetIQ_Job_Code`,`Supervisor_Name`         
      FROM Employee    
      ORDER BY `Fk_Business_Unit_Code`, `NetIQ_Job_Code`, `Supervisor_Name` DESC) z,  
(SELECT @row_num := 1) x,  
(SELECT @prev_value := '') y  
ORDER BY `Fk_Business_Unit_Code`, `NetIQ_Job_Code`,`Supervisor_Name` DESC

-3
set @i = 1;  
INSERT INTO ARG_VALUE_LOOKUP(ARG_VALUE_LOOKUP_ID,ARGUMENT_NAME,VALUE,DESCRIPTION,UPDATE_TIMESTAMP,UPDATE_USER,VER_NBR,OBJ_ID) 
select @i:= @i+1 as ARG_VALUE_LOOKUP_ID,ARGUMENT_NAME,VALUE,DESCRIPTION,CURRENT_TIMESTAMP,'admin',1,UUID() 
FROM TEMP_ARG_VALUE_LOOKUP 
order by ARGUMENT_NAME;

1
Vui lòng thử định dạng bất kỳ câu trả lời nào và đưa ra một số bối cảnh bổ sung như những gì bạn đang cố gắng thực hiện. Tại thời điểm này, không có gì khác ngoài văn bản được định dạng kém.
Yannick Meeus

2
Điều này dường như không có bất kỳ mối quan hệ với câu hỏi ban đầu. Nếu bạn có một câu hỏi của riêng bạn, xin vui lòng hỏi riêng.
Jeroen Mostert

-5
SELECT 
    col1, col2, 
    count(*) as intRow
FROM Table1
GROUP BY col1,col2
ORDER BY col3 desc
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.