source

MySQL의 ROW_NUMBER()

nicesource 2022. 11. 15. 21:38
반응형

MySQL의 ROW_NUMBER()

SQL Server 능 SQL Server sql SQL Server the 제 ? 까 법 은 니 습 있 is there 이 하방 way 좋 는 을 my복 in my replicate aROW_NUMBER()????

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

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

예를 들어, 음 다 를 어 한 건 가 습,있 then i수추 example,다니 to could for a조할 add들그예 limit,을 condition에제,intRow to 1 to get a single row with the highest 가장 높은 단일 행을 얻으려면 1로 이동합니다.col3 for each 각각에 대해서(col1, col2)쌍.

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

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

제 경우엔 어떻게 될까요?col1과 col2 각각에 대해 두 개의 변수가 필요합니다.col1이 바뀌면 col2를 리셋해야 하는데..?

예. Oracle이라면 LED 기능을 사용하여 다음 가치를 극대화할 수 있습니다.다행히 Quassnoi는 MySQL에서 구현해야 로직을 커버하고 있습니다.

각 (col1, col2) 쌍에 대해 가장 높은 col3가 1개 있는 행을 원합니다.

이는 그룹별 최대값으로 SQL에서 가장 자주 묻는 질문 중 하나입니다(쉽게 느껴지지만 실제로는 그렇지 않습니다).

나는 종종 null-self-join을 선호한다.

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,col2가 일치하는 다른 행이 없는 테이블 내의 행을 가져옵니다." (여러 행이 같은 col1,col2,col3을 갖는 경우 이 행과 다른 대부분의 groupwise-maximum 솔루션이 여러 행을 반환합니다.문제가 있으면 후처리가 필요할 수 있습니다.)

저는 항상 이 패턴을 따르게 됩니다.다음 표에 기재되어 있습니다.

+------+------+
|    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
SELECT 
    @i:=@i+1 AS iterator, 
    t.*
FROM 
    tablename AS t,
    (SELECT @i:=0) AS foo

터에서MySQL 8.0.0위의 창을 사용하여 기본 기능을 사용할 수 있습니다.그 이상에서는 윈도우 기능을 기본적으로 사용할 수 있습니다.

1.4 MySQL 8.0의 신기능:

창 기능

MySQL은 쿼리의 각 행에 대해 해당 행과 관련된 행을 사용하여 계산을 수행하는 창 함수를 지원합니다.여기에는 RANK(), LAG() 및 NTILLE() 등의 함수가 포함됩니다.또, 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 데모

이 문서를 참조해 주세요.MySQL에서 파티션을 사용하여 SQL ROW_NUMBER()를 모방하는 방법에 대해 설명합니다.WordPress Implementation에서 이와 똑같은 시나리오를 접했습니다.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개의 필드에 대해 테스트했습니다.이게 도움이 됐으면 좋겠다.이 질문에 대한 자세한 내용은 기사를 참조하십시오.

또한 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를 사용하지 않고 대신 중첩된 선택에 의존한다는 것입니다.

함수를 정의합니다.

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;

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

mysql의 row_number 쿼리

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

There is no funtion like 같은 기능은 없다.rownum,row_num()MySQL에서는 다음과 같은 방법이 다음과 같습니다.MySQL에서는 다음과 같습니다.

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

중요: MySQL 8+로 업그레이드하여 정의되고 문서화된 ROW_NUMBER() 함수를 사용하고 MySQL의 기능이 제한된 오래된 버전에 연결된 오래된 해커는 폐기하십시오.

여기 그 해킹 중 하나가 있습니다.

쿼리 내 변수를 사용하는 답변은 대부분 또는 모두 설명서에 다음과 같이 기재되어 있는 사실을 무시하는 것 같습니다(파라프레이스).

SELECT 목록의 항목이 위에서 아래로 평가되는 것에 의존하지 마십시오.한 SELECT 항목에 변수를 할당하지 않고 다른 항목에 사용

따라서, 그들은 잘못된 답을 양산할 위험이 있습니다. 왜냐하면 그들은 보통 잘못된 답을 하기 때문입니다.

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

이러한 값이 상향 평가될 경우 행 번호는 작동을 중지합니다(파티션 없음).

그래서 우리는 집행 순서가 보장된 무언가를 사용해야 합니다.대소문자 입력 시기:

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이 평가됩니다.이 행의 col이 이전 행의 col과 동일한 경우 @r이 증가하여 CASE에서 반환됩니다.이 반환 LED 값은 @r에 저장됩니다.할당이 @r에 할당된 새 값을 결과 행으로 반환하는 것은 MySQL의 기능입니다.

  • 결과 세트의 첫 번째 행에 대해 @prevcol은 늘(서브쿼리에서는 늘로 초기화됨)이므로 이 술어는 false입니다.이 첫 번째 술어는 col이 변경될 때마다 false를 반환합니다(현재 행은 이전 행과 다름).그러면 두 번째 WHEN이 평가됩니다.

  • 두 번째 WH의 술어는 항상 false이며 @prevcol에 새로운 값을 할당하기 위해 존재합니다.이 행의 col은 이전 행의 col과 다르기 때문에(같은 경우 첫 번째 WHEN이 사용되기 때문에 알고 있습니다), 다음 번 테스트를 위해 새 값을 할당해야 합니다.할당이 이루어진 후 할당 결과가 null과 비교되며 null에 해당하는 것은 모두 false이므로 이 술어는 항상 false입니다.그러나 적어도 이 행의 col 값을 유지하는 작업을 수행했기 때문에 다음 행의 col 값과 비교하여 평가할 수 있습니다.

  • 두 번째 WHEN은 false이기 때문에 (col)로 분할하고 있는 컬럼이 변경된 경우 @r에 새로운 값을 부여하고 번호부여를 1부터 재시작합니다.

이렇게 되면 다음과 같은 상황이 됩니다.

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중요한 것은 중요합니다.합 니 pc @ PCOLX에 Null을 할당하지 않고 작업 pc 으 들 하 고 동 요 작 을 당 다 중ol을ol you without

  • 결과 집합도 파티션 열별로 정렬해야 이전 열과 비교할 수 있습니다.따라서 한 열에 따라 RWNUMB를 정렬할 수는 없지만 결과 집합은 다른 열로 정렬할 수 있습니다. 서브쿼리로 해결할 수 있지만 LIMIT을 사용하지 않는 한 서브쿼리 순서가 무시될 수 있으며 이는 성능에 영향을 미칠 수 있다고 문서에서는 말합니다.

  • 메서드가 동작하는 것을 테스트하는 것 이외에는 상세하게 조사하지 않았지만, 두 번째 WHEN의 술어가 최적화되어(null과 비교되는 것은 모두 null/false이므로 굳이 할당을 실행하지 않아도 되는 경우)도 정지합니다.제 경험상으로는 이런 일이 일어나지 않는 것 같습니다만, 합리적으로 일어날 수 있다면 기꺼이 의견을 받아들여 해결책을 제안하겠습니다.

  • pc pc vPCLX pc 니 다 pc 할 습 명 ol 하 는 을 이 는 it may s 브 하 ol select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)

가장 잘 작동하는 솔루션은 다음과 같은 서브쿼리를 사용하는 것이었습니다.

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

파티션 기준 열은 '='과 비교되고 AND로 구분됩니다.ORDER BY 열은 '<' 또는 '>'와 비교되며 OR로 구분됩니다.

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

Rownumber 기능은 모방할 수 없습니다.기대했던 결과를 얻을 수도 있지만, 어느 단계에서 실망할 수도 있습니다.mysql 문서에 기재되어 있는 내용은 다음과 같습니다.

SELECT와 같은 다른 문장의 경우 예상한 결과를 얻을 수 있지만 이는 보장되지 않습니다.다음 문장에서 MySQL이 먼저 @a를 평가하고 다음으로 할당을 수행한다고 생각할 수 있습니다.SELECT @a, @a:=@a+1, ...; 단, 사용자 변수를 포함하는 식에 대한 평가 순서는 정의되어 있지 않습니다.

잘 부탁드립니다, 게오르기.

MariaDB 10.2는 RANK(), ROW_NUMBER() 및 기타 여러 가지를 포함한 "윈도 함수"를 구현하고 있습니다.

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

이번 달 Percona Live에서 한 강연에 따르면 상당히 잘 최적화되어 있습니다.

구문은 질문의 코드와 동일합니다.

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;

이를 통해 ROW_NUMBER() AND PARTION 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

"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을 사용하여 이러한 열을 다른 순서로 계산하면 작동하지 않을 수 있습니다.
  • 이 간단한 예에서는 1개만 넣지만 여러 개의 "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
    

이것도 해결책이 될 수 있습니다.

SET @row_number = 0;

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

여기서 DEXINK_RANK() 함수를 사용할 수 있다고 생각합니다.예:

select `score`, DENSE_RANK() OVER( ORDER BY score desc ) as `rank` from Scores;

https://www.mysqltutorial.org/mysql-window-functions/mysql-dense_rank-function/

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

행 간/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

또한 조금 늦었지만, 오늘은 같은 요구가 있었기 때문에 구글에서 검색해 보았습니다.마지막으로 Pinal Dave의 기사 http://blog.sqlauthority.com/2014/03/09/mysql-reset-row-number-for-each-group-partition-by-row-number/에 기재되어 있는 간단한 일반적인 접근법이 있습니다.

폴의 첫 번째 질문(그것도 제 문제였습니다)에 초점을 맞추고 싶었기 때문에 제 해결책을 실천 사례로 요약합니다.

2개의 컬럼으로 분할하고 싶기 때문에 반복 중에 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의 명확한 시연에 감사드립니다.

쿼리가 다음과 같은 경우 크로스 조인 및 콤마가 있는 솔루션은 작동하지 않습니다.GROUP BY진술.이러한 경우 하위 선택을 사용할 수 있습니다.

SELECT (@row_number := @row_number + 1) AS rowNumber, res.*
FROM
(
  SELECT SUM(r.amount) 
  FROM Results r 
  WHERE username = 1 
  GROUP BY r.amount
) res
CROSS JOIN (SELECT @row_number := 0) AS dummy

이것은 가장 견고한 솔루션은 아닙니다.다만, 몇개의 다른 값을 가지는 필드에 분할 순위를 작성하는 것을 검토하고 있는 경우, 필요한 만큼 변수를 가지는 로직의 경우, 사용하기 어려운 경우가 있습니다.

과거에도 이와 같은 것이 효과가 있었습니다.

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

그게 말이 됐으면 좋겠어/도움이 됐으면 좋겠어!

MySQL 버전8부터 ROW_NUMBER()를 지원하므로 SQL Server에서 사용하는 것처럼 다음 쿼리를 사용할 수 있습니다.

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

Maria DB 10.4.21에서도 테스트했습니다.그곳에서도 작동한다.

이 작업은 열이 여러 개일 때 RowNumber를 생성하기 위한 완벽한 작업입니다.이 경우 2열입니다.

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

언급URL : https://stackoverflow.com/questions/1895110/row-number-in-mysql

반응형