MySQL의 ROW_NUMBER ()


281

MySQL에서 SQL Server 기능을 복제하는 좋은 방법이 ROW_NUMBER()있습니까?

예를 들면 다음과 같습니다.

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

그런 다음 예를 들어 조건 intRow을 1로 제한 하여 col3(col1, col2)쌍 마다 가장 높은 단일 행을 얻을 수 있습니다.


9
greatest-n-per-group유사한 질문으로 안내하기 위해 태그가 지정 되었습니다.
Bill Karwin 2009

1
Sql-Server이 태그는 결합 된 태그 검색 에서 가장 많이 투표 된 항목 이지만 실제로 SQL Server와 관련이없는 태그를 제거했습니다 .
Martin Smith


MySQL의 경우 유일한 효율적인 솔루션은 mysql.rjweb.org/doc.php/groupwise_max에 설명되어 있습니다 . 많은 공개 솔루션에는 전체 테이블 스캔이 필요합니다.
Rick James

2
MySQL에서 실제 분석 함수 ROW_NUMBER, RANK, DESNSE_RANK 를 위한 뻔뻔한 자체 홍보 솔루션
Kenneth Xu

답변:


102

각 (col1, col2) 쌍마다 단일 최고 col3 행을 원합니다.

그것은 가장 일반적으로 묻는 SQL 질문 중 하나 인 그룹 별 최대 값입니다 (쉽게 보이지만 실제로는 그렇지 않기 때문에).

나는 종종 빈약 한 자조에 빠졌다.

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;

"일치하는 col1을 가진 다른 행이없는 테이블의 행을 가져옵니다. (이것과 대부분의 다른 그룹 별 최대 솔루션은 하나 이상의 행에 동일한 col1, col2, col3이 있으면 여러 행을 반환합니다. 문제가 있으면 사후 처리가 필요할 수 있습니다.)


2
그러나 (col1, col2) 쌍에 대해 최대 3 개의 col3 값이 있으면 어떻게됩니까? 두 줄로 끝납니다.
Paul

@ 폴 : 예! 방금 전에 답에 메모를 추가했습니다. 당신은 일반적으로 쉽게 어떤 임의의 기준으로 이후 응용 계층에서 원치 않는 여분의 행을 삭제할 수 있지만,이 경우 많은 같은 COL3이있는 행 모두를이 문제가 될 수 있습니다.
bobince

1
bobince, 솔루션이 여기에서 다소 인기를 얻었지만 질문이 있습니다. 해결책은 기본적으로 누군가가 다음 쿼리를 사용하여 가장 큰 ID를 찾으려고하는 것과 같습니다. 단일 행을 찾기 SELECT t1.id FROM test t1 LEFT JOIN test t2 ON t1.id>t2.id WHERE t2.id IS NULL;위해 n*n/2 + n/2IS NULL 비교가 필요하지 않습니까? 내가 보지 못한 최적화가 발생합니까? 다른 스레드에서 Bill에게 비슷한 질문을하려고했지만 그는 무시한 것 같습니다.
newtover

2
@Paul-그룹당 ​​최대 값과 일치하는 여러 행이 존재하고 하나만 잡고 자하는 경우, 항상 ON 절 논리에 기본 키를 추가하여 연결을 끊을 수 있습니다. SELECT t0.col3 FROM 테이블 AS t0 LEFT JOIN 테이블 AS t1 ON t0.col1 = t1.col1 및 t0.col2 = t1.col2 AND (t1.col3, t1.pk)> (t0.col3, t0.pk) t1.col1은 NULL입니다.
존 암스트롱-Xgc

2
이 같은 더 읽을 것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

MySQL에는 순위 기능이 없습니다. 가장 가까운 것은 변수를 사용하는 것입니다.

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

내 경우에는 어떻게 작동합니까? col1과 col2 각각에 하나씩 두 개의 변수가 필요합니까? col1이 변경되었을 때 Col2가 어떻게 든 재설정되어야합니까?

예. Oracle 인 경우 LEAD 함수를 사용하여 다음 값에서 정점에 도달 할 수 있습니다. 고맙게도, Quassnoi는 MySQL 구현에 필요한 로직을 다룹니다 .


1
흠 .... 그래서 내 경우에는 어떻게 작동합니까? col1과 col2 각각에 하나씩 두 개의 변수가 필요합니까? col1이 변경되었을 때 Col2가 어떻게 든 재설정되어야합니까?
Paul

제가 위에서 말했듯이 덕분에 ...,이 답변이 동등하게 bobince의 허용됩니다,하지만 난 단 하나의 :-) 체크 할 수 있습니다

9
동일한 명령문에서 사용자 정의 변수에 지정하고 읽는 것은 신뢰할 수 없습니다. 이 여기에 설명되어 있습니다 : dev.mysql.com/doc/refman/5.0/en/user-variables.html : "일반적으로, 당신은 사용자 변수에 값을 할당하지 않고 동일한 명령문 내에서 값을 읽어 안됩니다. 예상 한 결과를 얻을 수 있지만 이것이 보장되지는 않습니다. 사용자 변수와 관련된 표현식의 평가 순서는 정의되어 있지 않으며 주어진 명령문에 포함 된 요소에 따라 변경 될 수 있습니다. "
Roland Bouman

1
@ 롤랜드 : 작은 데이터 세트에서만 테스트했지만 아무런 문제가 없었습니다. 너무 나쁜 MySQL은 아직 기능을 해결하지 못했습니다. 2008 년 이후 요청이있었습니다
OMG Ponies

2
이것은 롤랜드가 지적한 것처럼 정의되지 않은 행동으로 보입니다. 예를 들어 이것은 시도한 테이블에 대해 완전히 잘못된 결과를 제공합니다.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

나는 항상이 패턴을 따릅니다. 이 테이블이 주어지면 :

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

이 결과를 얻을 수 있습니다 :

+------+------+------------+
|    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 |
+------+------+------------+

이 쿼리를 실행하면 변수를 정의 할 필요가 없습니다.

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

희망이 도움이됩니다!


1
열이 VARCHAR 또는 CHAR 인 경우이 구조로이를 처리 할 수 ​​있습니까?
Tushar

3
당신은 최고 Mosty, 나는 이것을 정확하게 찾고 있습니다
luckykrrish

row_number에 대한 논리를 사용 하여이 답변 을 주었습니다 . 감사.
Utsav

운영자 @Tushar <, >, <=, >=핸들 CHAR 및 알파벳 순서에 VARCHAR 데이터 유형; 나는 당신이 찾고있는 것입니다.
alex

1
@AlmazVildanov 당신은이 쿼리를 필터의 하위 쿼리로 간단하게 사용할 수 있어야 하며이 row_numbers <= 2 답변에 대단히 감사드립니다.
Zax

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

1
첫 번째 : = @OMG Ponies 답변에서 누락 된 것 같습니다. Peter Johnson을 게시 해 주셔서 감사합니다.
sholsinger

AS foo가 FROM 문의 첫 번째 테이블이어야한다고 생각합니다. 특히 다른 테이블에서 하위 선택을 사용하는 경우
andig

왜 '.. as foo'가 필요한가요?
Tom Chiverton

@TomChiverton 누락 된 경우 다음과 같은 결과가 나타납니다. "오류 코드 : 1248. 모든 파생 테이블에는 고유 한 별칭이 있어야합니다"
ExStackChanger

1
여기에 순위 할당이 완전히 정의되어 있지 않으며 이것은 질문에 대답조차하지 않습니다
jberryman

27

이 기사에서 MySQL의 파티션으로 SQL ROW_NUMBER ()를 모방하는 방법을 보여줍니다. WordPress 구현 에서이 동일한 시나리오에 부딪 쳤습니다. ROW_NUMBER ()가 필요했지만 거기에 없었습니다.

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

이 기사의 예제는 필드 별 단일 파티션을 사용하는 것입니다. 추가 필드로 파티션을 나누려면 다음과 같이 할 수 있습니다.

  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 

concat_ws를 사용하면 null을 처리합니다. int, date 및 varchar을 사용하여 3 개의 필드에 대해 이것을 테스트했습니다. 도움이 되었기를 바랍니다. 이 쿼리를 분류하고 설명하는 기사를 확인하십시오.


1
대박. 이것은 실제로 파티셔닝을 수행합니다. 매우 편리함
Stuart Watt

1
자체 조인과 비교하면 훨씬 효율적이지만 논리에 문제가 있으므로 row_num을 계산하기 전에 순서가 발생해야하며 concat도 필요하지 않습니다. ```SELECT @row_num : = IF (@ prev_col1 = t.col1 AND @ prev_col2 = t.col2), @ row_num + 1, 1) AS RowNumber, t.col1, t.col2, t.col3, t.col4 , @ prev_col1 : = t.col1, @ prev_col2 : = t.col2 FROM (SELECT * FROM table1 ORDER BY col1, col2, col3) t, (SELECT @row_num : = 1, @ prev_col1 : = '', @ prev_col2 : = '') VAR```
케네스 쑤

이것을 하위 쿼리에 넣어야하는 경우 limit 18446744073709551615강제 order by절에 추가하십시오 .
xmedeko

concat_ws빈 문자열 ''로 위험합니다 : concat_ws('',12,3) = concat_ws('',1,23). 분리기를 사용 '_'하거나 @Kenneth Xu 솔루션을 사용하는 것이 좋습니다.
xmedeko

op의 연결이 끊어졌습니다. 여기 링크의 아카이브
2426679

25

에서 MySQL 8.0.0이상 당신은 기본적으로 윈도우 기능을 사용할 수 있습니다.

1.4 MySQL 8.0의 새로운 기능 :

창 기능.

MySQL은 이제 쿼리의 각 행에 대해 해당 행과 관련된 행을 사용하여 계산을 수행하는 창 기능을 지원합니다. 여기에는 RANK (), LAG () 및 NTILE ()와 같은 함수가 포함됩니다. 또한 기존의 여러 집계 함수를 창 함수로 사용할 수 있습니다. 예를 들어 SUM () 및 AVG ()입니다.

ROW_NUMBER () over_clause :

파티션 내의 현재 행 번호를 반환합니다. 행 번호 범위는 1에서 파티션 행 수까지입니다.

ORDER BY는 행의 번호가 매겨지는 순서에 영향을줍니다. ORDER BY가 없으면 행 번호가 결정되지 않습니다.

데모:

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;

DBFiddle 데모


1
한숨 ... 마침내!
Used_By_Already

15

또한 쿼리 코드를 약간 수정하여 Mosty Mostacho의 솔루션에 투표했습니다.

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

동일한 결과를 얻을 것입니다 :

+------+------+------------+
|    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 |
+------+------+------------+

테이블 :

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

쿼리에서 JOIN과 GROUP BY를 사용하지 않는 유일한 차이점은 중첩 된 선택에 의존합니다.


이것이 더 좋을까요? 둘 다 이차적 인 것처럼 보일지 모르지만 EXPLAIN 출력을 해석하는 방법을 잘 모르겠습니다
jberryman

사실, 중첩 선택은 MySQL에서 잘 최적화되지 않은 것으로 알려져 있으므로이 답변은 쿼리 기술을 시연하기위한 것입니다. 위의 변수 기반 예제는 대부분의 실제 사례에서 더 잘 작동한다고 생각합니다.
abcdn

1
변수 기반 답변이 실제로 정의 된 동작을 사용하고 있다고 확신하지 않습니다 ...
jberryman

죄송합니다. "정의 된 행동"의 의미를 잘 모르겠습니다. 그것이 효과가 없거나 문서화되지 않았다고 걱정하고 있습니까?
abcdn

1
"정의되지 않은 행동"은 작동하도록 문서화되지 않았거나 작동을 보증하지 않는 것으로 문서화되었음을 의미합니다. 이 페이지의 주석에서 인용문 및 링크를 참조하십시오. 그것은 (소리없이) 원하는 / 추측 / 가설 / 판타지 크기를 반환 할 수 있습니다 . 구현의 특정 버전에서 CASE 증분 및 변수 사용을 사용하는 특정 쿼리 표현식은 Percona의 프로그래머가 코드를 살펴 보는 방식으로 작동하는 것으로 나타났습니다. 릴리스에 따라 변경 될 수 있습니다.
philipxy

12

함수를 정의합니다.

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

그때 할 수 있습니다 :

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

이제 뷰에서 가질 수없는 하위 쿼리가 없습니다.


한 가지 제한 사항으로 작동합니다. 쿼리를 여러 번 실행하면 동일한 결과 집합에 대해 점점 증가하는 fakeId가 발생합니다.
Stephan Richter

당신은 set @fakeId = 0; 최적이 아니라 작동하는 쿼리를 실행할 때마다
jmpeace

DETERMINISTIC을 제거하면 정말 이상한 문제가 발생합니다. 그런 다음 order by를 사용할 때 fakeId가 올바르지 않습니다. 왜 이런거야?
Chris Muench

8

mysql에서 row_number 쿼리

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

UPDATE 쿼리에서 사용할 수 있습니까? 노력하고 있지만 "데이터가 잘려서 열 ..."오류가 발생합니다.
Diego

1
누구든지 UPDATE에서 사용하고 싶다면 작업을 위해 하위 쿼리로 사용해야합니다. 업데이트 <테이블> SET <필드> = (선택 \ @row_number : = \ @row_number +1) ORDER BY <주문 열>; 순서 열은 행의 값 순서를 결정합니다.
Diego

8

이 같은 더 연료 소모량이 없으며 rownum, row_num()MySQL의에 있지만, 주변의 방법은 다음과 같다 :

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

4

내가 가장 잘 작동하는 해결책은 다음과 같은 하위 쿼리를 사용하는 것입니다.

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

PARTITION BY 열은 '='와 비교되고 AND로 구분됩니다. ORDER BY 열은 '<'또는 '>'와 비교되고 OR로 구분됩니다.

조금 비싸더라도 매우 유연하다는 것을 알았습니다.


4

행 번호 기능을 흉내낼 수 없습니다. 예상 한 결과를 얻을 수 있지만 어떤 단계에서는 실망 할 것입니다. 다음은 mysql 문서의 내용입니다.

SELECT와 같은 다른 명령문의 경우 예상 한 결과를 얻을 수 있지만 이것이 보장되지는 않습니다. 다음 문장에서 MySQL이 @a를 먼저 평가 한 다음 두 번째 할당을 수행한다고 생각할 수 있습니다. SELECT @a, @a : = @ a + 1, ...; 그러나 사용자 변수와 관련된 표현식의 평가 순서는 정의되어 있지 않습니다.

감사합니다.


나는 따르지 않는다. "@i : = @i + 1을 위치로"는 "ROW_NUMBER ()를 (sum (score) desc로 정렬) 위치를 대신하여"대체하는 방법이 아닌가?
Tom Chiverton

1
@TomChiverton 매뉴얼이 말한 것처럼 동작이 정의되어 있지 않기 때문입니다.
philipxy


2

"PARTITION BY"부분을 다루는 간단한 답변이 없으므로 여기에 내 것이 있습니다.

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
  • ORDER BY 절은 ROW_NUMBER 요구 사항을 반영해야합니다. 따라서 이미 명확한 제한이 있습니다.이 양식의 ROW_NUMBER 개의 "에뮬레이션"을 동시에 가질 수 없습니다.
  • "계산 열"의 순서는 중요 합니다. mysql이 다른 순서로 해당 열을 계산하면 작동하지 않을 수 있습니다.
  • 이 간단한 예에서는 하나만 넣었지만 여러 개의 "PARTITION BY"파트를 가질 수 있습니다

        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

조금 늦었지만 답변을 찾는 사람에게도 도움이 될 수 있습니다 ...

rows / row_number 예-모든 SQL에서 사용될 수있는 재귀 쿼리 :

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
미안하지만 내가 아는 한 MySQL은 일반적인 테이블 표현식을 지원하지 않습니다 .
Álvaro González

이 대답은 정말 이전의 MySQL 버전에서 사용하기에 이해가되지 않도록 ÁlvaroGonzález MySQL은 8 만 .. CTE 및 창 기능을 지원 @ 그것은 ... 지금 않습니다
레이몬드 Nijland

1

이를 통해 ROW_NUMBER () 및 PARTITION BY와 동일한 기능을 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

또한 조금 늦었지만 오늘도 같은 필요성이 있었기 때문에 Google에서 검색했으며 마침내 Pinal Dave의 기사 http://blog.sqlauthority.com/2014/03/09/mysql-reset-row 에서 간단한 일반 접근 방식을 검색했습니다. -각 그룹 별 파티션-번호-행 번호 /

나는 Paul의 원래 질문 (내 문제이기도 함)에 집중하고 싶었으므로 솔루션을 실제 예제로 요약합니다.

두 그룹으로 분할하고 싶기 때문에 반복 중에 SET 변수를 작성하여 새 그룹이 시작되었는지 여부를 식별합니다.

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은 MAKE_SET의 첫 번째 매개 변수에서 SET (3 = 1 | 2)의 두 값을 원한다는 것을 의미합니다. 물론 그룹을 구성하는 두 개 이상의 열이 없으면 MAKE_SET 연산을 제거 할 수 있습니다. 구조는 정확히 동일합니다. 이것은 필요에 따라 나를 위해 일하고 있습니다. 그의 명확한 시위에 대한 Pinal Dave에게 감사드립니다.


1
참고 ORDER BY하위 쿼리에서 무시 될 수있다 (참조 mariadb.com/kb/en/mariadb/...을 ). 이에 대한 제안 된 솔루션 LIMIT 18446744073709551615은 하위 쿼리 에 추가 하여 강제로 정렬하는 것입니다. 그러나이 성능 문제가 발생할 정말 큰 테이블 : 괴물이 유효하지 않습니다 수
pnomolos을

1

이것은 또한 해결책이 될 수 있습니다.

SET @row_number = 0;

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

파티션을하지는 않으며, 인용 된 답변과 크게 다르지 않습니다
Caius Jard

1

MySQL은 8.0+ 버전부터 ROW_NUMBER ()를 지원했습니다 .

MySQL 8.0 이상을 사용하는 경우 ROW_NUMBER () 함수를 확인하십시오. 그렇지 않으면 ROW_NUMBER () 함수를 에뮬레이션했습니다.

row_number ()는 첫 번째 행에 대해 1부터 시작하여 행의 순차 번호를 리턴하는 순위 함수입니다.

이전 버전의 경우

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

1

중요 : MySQL 8 이상으로 업그레이드하고 정의되고 문서화 된 ROW_NUMBER () 함수를 사용하고 기능 제한 고대 버전의 MySQL에 연결된 오래된 해킹을 제거하십시오

이제 해킹 중 하나가 있습니다.

쿼리 내 변수를 주로 / 모두 사용하는 여기에 대한 답변은 설명서에서 다음과 같이 말한 사실을 무시하는 것 같습니다.

SELECT 목록의 항목을 위에서 아래로 순서대로 의존하지 마십시오. 하나의 SELECT 항목에 변수를 할당하지 않고 다른 항목에 사용하십시오.

따라서, 그들은 일반적으로 잘못하기 때문에 잘못된 답변을 얻을 수있는 위험이 있습니다

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

이것들이 상향식으로 평가되면 행 번호가 작동을 멈 춥니 다 (파티션 없음)

따라서 보장 된 실행 순서로 무언가를 사용해야합니다. 다음 경우에 CASE를 입력하십시오.

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

개요 ld로서 prevcol의 할당 순서가 중요합니다. prevcol은 현재 행의 값을 할당하기 전에 현재 행의 값과 비교해야합니다 (그렇지 않으면 이전 행의 col 값이 아닌 현재 행 col 값이 됨). .

이것이 함께 맞는 방법은 다음과 같습니다.

  • 첫 번째 WHEN이 평가됩니다. 이 행의 열이 이전 행의 열과 동일하면 @r이 증가하여 CASE에서 반환됩니다. 이 리턴 LED 값은 @r에 저장됩니다. 할당은 @r에 할당 된 값의 새로운 값을 결과 행으로 반환하는 MySQL의 기능입니다.

  • 결과 세트의 첫 번째 행의 경우 @prevcol이 널 (서브 쿼리에서 널로 초기화 됨)이므로이 술어는 false입니다. 이 첫 번째 술어는 col이 변경 될 때마다 false를 리턴합니다 (현재 행이 이전 행과 다릅니다). 이로 인해 두 번째 WHEN이 평가됩니다.

  • 두 번째 WHEN 술어는 항상 false이며 @prevcol에 새 값을 지정하는 것만으로 존재합니다. 이 행의 열이 이전 행의 열과 다르기 때문에 (이것이 동일하면 첫 번째 WHEN이 사용 되었기 때문에이를 알고 있음) 다음에 테스트 할 수 있도록 새 값을 할당해야합니다. 대입이 이루어진 후 대입 결과가 널과 비교되고 널과 동등한 것은 거짓이므로이 술어는 항상 거짓입니다. 그러나 적어도 평가하면이 행에서 col 값을 유지하는 작업을 수행 했으므로 다음 행의 col 값과 비교하여 평가할 수 있습니다

  • 두 번째 WHEN이 false이므로 이는 (col)로 분할하는 열이 변경된 상황에서 @r에 새 값을 제공하고 번호 매기기를 1에서 다시 시작하는 것이 ELSE입니다.

우리는 이것이 다음과 같은 상황에 도달합니다.

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

일반적인 형태가 있습니다 :

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

각주 :

  • pcol의 p는 "파티션"을 의미하고 ocol의 o는 "순서"를 의미합니다. 일반적인 형태에서는 변수 이름에서 "prev"를 삭제하여 시각적 혼란을 줄였습니다.

  • 괄호 (@pcolX := colX) = null가 중요합니다. 그것들이 없으면 @pcolX에 null을 할당하고 일이 중단됩니다.

  • 이전 열을 비교하기 위해 결과 열을 파티션 열별로 정렬해야한다는 것은 타협입니다. 따라서 한 열에 따라 행 번호를 정렬 할 수 없지만 결과 집합을 다른 열에 정렬 할 수는 없습니다. 하위 쿼리를 사용 하여이 문제를 해결할 수는 있지만 문서는 LIMIT를 사용하지 않으면 하위 쿼리 순서가 무시 될 수 있다고 생각합니다. 공연

  • 메소드가 작동하는지 테스트하는 것 이상으로 탐구하지는 않았지만 두 번째 WHEN의 술어가 최적화 될 위험이있는 경우 (null과 비교되는 것은 null / false이므로 할당을 실행하는 이유는 무엇입니까?) 또한 중지합니다. 이것은 내 경험에서 발생하지 않는 것 같지만 합리적으로 발생할 수 있다면 기꺼이 의견을 수락하고 해결책을 제안합니다.

  • @pcolX 변수를 생성하는 하위 쿼리에서 @pcolX를 생성하는 null을 실제 유형의 열로 캐스팅하는 것이 좋습니다. select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)


이에 대한 정당성은 없다. 같은 변수에 할당하고 읽는 다른 답변과 마찬가지로.
philipxy

더 자세한 필을 제공 할 수 있습니까?
Caius Jard

이 페이지에 대한 다른 의견을 참조하십시오. 인터넷 검색 '사이트 : stackoverflow.com "philipxy"MySQL의 변수 (세트 또는 할당하거나 할당 또는 쓰기) 읽기' 의 답을 내게로버그보고 저에 의해 코멘트에 연결 이 질문에 허용 대답은 아직 즉시 수동 인용 곳 주장에서 모순되는 일을하는 것은 괜찮습니다. 수동 재 변수 및 재 지정을 읽으십시오.
philipxy


귀하의 우려를 이해합니다
Caius Jard

0

이것은 가장 강력한 솔루션은 아니지만 몇 가지 다른 값을 가진 필드에서 분할 된 순위를 만들려는 경우 필요한만큼 많은 변수를 가진 논리가있을 때 어떤 경우를 사용하는 것이 어려울 수 있습니다.

이와 같은 것이 과거에 저에게 효과적이었습니다.

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>
;

이해가 되길 바랍니다.


-1

이것은 하나 이상의 열이있을 때 RowNumber를 만드는 데 완벽하게 작동합니다. 이 경우 두 열입니다.

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
답변의 형식을 지정하고 수행하려는 작업에 대한 추가 컨텍스트를 제공하십시오. 현재는 형식이 잘못된 텍스트 일뿐입니다.
Yannick Meeus 12

2
원래 질문과 관련이없는 것 같습니다. 궁금한 점이 있으면 별도로 문의하십시오.
Jeroen Mostert

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.