MySQL의 ROW_NUMBER ()
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)
쌍 마다 가장 높은 단일 행을 얻을 수 있습니다.
각 (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이 있으면 여러 행을 반환합니다. 문제가 있으면 사후 처리가 필요할 수 있습니다.)
MySQL에는 순위 기능이 없습니다. 가장 가까운 것은 변수를 사용하는 것입니다.
SELECT t.*,
@rownum := @rownum + 1 AS rank
FROM YOUR_TABLE t,
(SELECT @rownum := 0) r
내 경우에는 어떻게 작동합니까? col1과 col2 각각에 하나씩 두 개의 변수가 필요합니까? col1이 변경되었을 때 Col2가 어떻게 든 재설정되어야합니까?
예. Oracle 인 경우 LEAD 함수를 사용하여 다음 값에서 정점에 도달 할 수 있습니다. 고맙게도, Quassnoi는 MySQL 구현에 필요한 로직을 다룹니다 .
나는 항상이 패턴을 따릅니다. 이 테이블이 주어지면 :
+------+------+
| 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의 파티션으로 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 개의 필드에 대해 이것을 테스트했습니다. 도움이 되었기를 바랍니다. 이 쿼리를 분류하고 설명하는 기사를 확인하십시오.
에서 MySQL 8.0.0
이상 당신은 기본적으로 윈도우 기능을 사용할 수 있습니다.
창 기능.
MySQL은 이제 쿼리의 각 행에 대해 해당 행과 관련된 행을 사용하여 계산을 수행하는 창 기능을 지원합니다. 여기에는 RANK (), LAG () 및 NTILE ()와 같은 함수가 포함됩니다. 또한 기존의 여러 집계 함수를 창 함수로 사용할 수 있습니다. 예를 들어 SUM () 및 AVG ()입니다.
파티션 내의 현재 행 번호를 반환합니다. 행 번호 범위는 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;
또한 쿼리 코드를 약간 수정하여 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
이 같은 더 연료 소모량이 없으며 rownum
, row_num()
MySQL의에 있지만, 주변의 방법은 다음과 같다 :
select
@s:=@s+1 serial_no,
tbl.*
from my_table tbl, (select @s:=0) as s;
내가 가장 잘 작동하는 해결책은 다음과 같은 하위 쿼리를 사용하는 것입니다.
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로 구분됩니다.
약간 비싸더라도 매우 유연하다는 것을 알았습니다.
행 번호 기능을 흉내낼 수 없습니다. 예상 한 결과를 얻을 수 있지만 어떤 단계에서는 실망 할 것입니다. 다음은 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의 강연을 바탕으로 합리적으로 최적화되었습니다.
구문은 질문의 코드와 동일합니다.
"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
조금 늦었지만 답변을 찾는 사람에게도 도움이 될 수 있습니다 ...
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
이를 통해 MySQL에서 ROW_NUMBER () 및 PARTITION BY와 동일한 기능을 수행 할 수 있습니다.
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
또한 조금 늦었지만 오늘도 같은 필요성이 있었기 때문에 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에게 감사드립니다.
이것은 또한 해결책이 될 수 있습니다.
SET @row_number = 0;
SELECT
(@row_number:=@row_number + 1) AS num, firstName, lastName
FROM
employees
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;
중요 : 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)
이것은 하나 이상의 열이있을 때 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
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;
SELECT
col1, col2,
count(*) as intRow
FROM Table1
GROUP BY col1,col2
ORDER BY col3 desc
참고 URL : https://stackoverflow.com/questions/1895110/row-number-in-mysql
'Programing' 카테고리의 다른 글
스캐너 대 버퍼 리더 (0) | 2020.03.29 |
---|---|
처음 발생시 분할 (0) | 2020.03.29 |
diff 명령에 해당하는 Windows 란 무엇입니까? (0) | 2020.03.29 |
세트를 변환하는 방법 (0) | 2020.03.29 |
SELECT로 삽입 (0) | 2020.03.29 |