[SQL] 각 그룹의 상위 1 행을 가져 오기
SQL각 그룹의 상위 1 행을 가져 오기
나는 각 그룹에 대한 최신 항목을 얻으려면 테이블을 가지고있다. 다음 표는 다음과 같습니다
DocumentStatusLogs 표
|ID| DocumentID | Status | DateCreated |
| 2| 1 | S1 | 7/29/2011 |
| 3| 1 | S2 | 7/30/2011 |
| 6| 1 | S1 | 8/02/2011 |
| 1| 2 | S1 | 7/28/2011 |
| 4| 2 | S2 | 7/30/2011 |
| 5| 2 | S3 | 8/01/2011 |
| 6| 3 | S1 | 8/02/2011 |
표는 DocumentID별로 그룹화 및 내림차순으로 DateCreated에 의해 정렬됩니다. 각 DocumentID, 나는 최신 상태를 싶어.
나의 선호 출력 :
| DocumentID | Status | DateCreated |
| 1 | S1 | 8/02/2011 |
| 2 | S3 | 8/01/2011 |
| 3 | S1 | 8/02/2011 |
자세한 내용은 부모 테이블을 참조하십시오 :
현재 문서 표
| DocumentID | Title | Content | DateCreated |
| 1 | TitleA | ... | ... |
| 2 | TitleB | ... | ... |
| 3 | TitleC | ... | ... |
부모 테이블 내가 쉽게 상태를 액세스 할 수있는이과 같이해야 하는가?
| DocumentID | Title | Content | DateCreated | CurrentStatus |
| 1 | TitleA | ... | ... | s1 |
| 2 | TitleB | ... | ... | s3 |
| 3 | TitleC | ... | ... | s1 |
최신 정보 난 그냥 쉽게 이러한 문제를 해결 할 수있는 "적용"을 사용하는 방법을 배웠습니다.
해결법
-
==============================
1.
;WITH cte AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC) AS rn FROM DocumentStatusLogs ) SELECT * FROM cte WHERE rn = 1
당신은 하루에 2 개 항목을 기대한다면,이 임의로 하나를 선택합니다. 하루 두 항목을 얻으려면, 대신 DENSE_RANK를 사용
정규화에 대한 여부를 당신이 원한다면, 그것은 의존한다 :
약자로, 당신은 상태 기록을 보존 할 수 있습니다. 당신이 (denormalisation 인)도 부모 테이블에서 최신 상태를 원하는 경우에 당신은 부모의 "상태"를 유지하기 위해 트리거를 필요 했어. 또는이 상태 기록 테이블을 놓습니다.
-
==============================
2.난 그냥 교차 사용에 적용하는 방법을 배웠습니다. 다음은이 시나리오에서 그것을 사용하는 방법은 다음과 같습니다
난 그냥 교차 사용에 적용하는 방법을 배웠습니다. 다음은이 시나리오에서 그것을 사용하는 방법은 다음과 같습니다
select d.DocumentID, ds.Status, ds.DateCreated from Documents as d cross apply (select top 1 Status, DateCreated from DocumentStatusLogs where DocumentID = d.DocumentId order by DateCreated desc) as ds
-
==============================
3.테이블과를 사용하여,이 테스트는 2008 R2 SQL 서버에 대해 실행 된 적용 나는 여기에 다양한 권고를 통해 어떤 타이밍 짓을했는지, 그 결과는 정말 관련된 테이블의 크기에 달려 있지만, 가장 일관된 솔루션은 CROSS을 사용하고 있습니다 137000000 개 기록과 6,500 기록하고, 다른 (동일한 스키마). 조회되는 열은 테이블 기본 키의 일부이며, 테이블 폭 (30 바이트에 대해) 매우 작다. 시간은 실제 실행 계획에서 SQL Server가보고됩니다.
테이블과를 사용하여,이 테스트는 2008 R2 SQL 서버에 대해 실행 된 적용 나는 여기에 다양한 권고를 통해 어떤 타이밍 짓을했는지, 그 결과는 정말 관련된 테이블의 크기에 달려 있지만, 가장 일관된 솔루션은 CROSS을 사용하고 있습니다 137000000 개 기록과 6,500 기록하고, 다른 (동일한 스키마). 조회되는 열은 테이블 기본 키의 일부이며, 테이블 폭 (30 바이트에 대해) 매우 작다. 시간은 실제 실행 계획에서 SQL Server가보고됩니다.
Query Time for 6500 (ms) Time for 137M(ms) CROSS APPLY 17.9 17.9 SELECT WHERE col = (SELECT MAX(COL)…) 6.6 854.4 DENSE_RANK() OVER PARTITION 6.6 907.1
나는 정말 놀라운 일이 십자가에 관계없이 포함 된 행 수의 적용에 대한 시간이 얼마나 일치했다 생각합니다.
-
==============================
4.나는 이것이 옛 스레드이지만 TIES 솔루션과 상위 1은 아주 좋은 및 솔루션을 통해 일부 읽기에 도움이 될 수 알고있다.
나는 이것이 옛 스레드이지만 TIES 솔루션과 상위 1은 아주 좋은 및 솔루션을 통해 일부 읽기에 도움이 될 수 알고있다.
select top 1 with ties DocumentID ,Status ,DateCreated from DocumentStatusLogs order by row_number() over (partition by DocumentID order by DateCreated desc)
더 TOP 절에 대해 여기에서 찾을 수 있습니다.
-
==============================
5.
SELECT * FROM DocumentStatusLogs JOIN ( SELECT DocumentID, MAX(DateCreated) DateCreated FROM DocumentStatusLogs GROUP BY DocumentID ) max_date USING (DocumentID, DateCreated)
어떤 데이터베이스 서버? 이 코드는 모두 작동하지 않습니다.
질문의 두 번째 절반에 관해서는, 열로 상태를 포함하는 나에게 합리적인 것 같다. 당신은 로그로 DocumentStatusLogs을 떠나지 만, 여전히 기본 테이블의 최신 정보를 저장할 수 있습니다.
BTW, 당신은 이미 (한 DateCreated가 DocumentStatusLogs에서 고유로) 당신이 단지를 사용하여 DocumentStatusLogs에 가입 할 수있는 문서 테이블에서 DateCreated 열이있는 경우.
편집 : MSSQL 너무로 변경, 사용을 지원하지 않습니다 :
ON DocumentStatusLogs.DocumentID = max_date.DocumentID AND DocumentStatusLogs.DateCreated = max_date.DateCreated
-
==============================
6.성능에 대해 걱정하는 경우, 당신은 또한 MAX ()와 함께이 작업을 수행 할 수 있습니다
성능에 대해 걱정하는 경우, 당신은 또한 MAX ()와 함께이 작업을 수행 할 수 있습니다
SELECT * FROM DocumentStatusLogs D WHERE DateCreated = (SELECT MAX(DateCreated) FROM DocumentStatusLogs WHERE ID = D.ID)
MAX하지 않는 반면, ROW_NUMBER ()는 당신의 SELECT 문에서 행 모든 정렬을 필요로한다. 크게 쿼리 속도를해야합니다.
-
==============================
7.이것은 아주 오래된 스레드,하지만 난 허용 대답은 나를 위해 특히 잘 작동하지 않았다 나는 그냥 같은 내 두 센트를 던질 거라고 생각했다. 나는 (> SQL 서버 2012 년 500 만 개 플러스 기록 45 초)으로 큰 데이터 세트에 GBN의 솔루션을 시도하고 몹시 느린 발견했다. 실행 계획을 보면이 문제가 크게 일을 느리게 정렬 작업이 필요하다는 것을 분명하다.
이것은 아주 오래된 스레드,하지만 난 허용 대답은 나를 위해 특히 잘 작동하지 않았다 나는 그냥 같은 내 두 센트를 던질 거라고 생각했다. 나는 (> SQL 서버 2012 년 500 만 개 플러스 기록 45 초)으로 큰 데이터 세트에 GBN의 솔루션을 시도하고 몹시 느린 발견했다. 실행 계획을 보면이 문제가 크게 일을 느리게 정렬 작업이 필요하다는 것을 분명하다.
여기에 내가 더 정렬 작업이 필요하지 않고 클러스터되지 않은 인덱스 검색을 수행하는 엔티티 프레임 워크에서 해제하는 것이 대안이다. 이는 전술 한 레코드 세트 2초 <의 실행 시간을 아래로 감소시킨다.
SELECT [Limit1].[DocumentID] AS [DocumentID], [Limit1].[Status] AS [Status], [Limit1].[DateCreated] AS [DateCreated] FROM (SELECT DISTINCT [Extent1].[DocumentID] AS [DocumentID] FROM [dbo].[DocumentStatusLogs] AS [Extent1]) AS [Distinct1] OUTER APPLY (SELECT TOP (1) [Project2].[ID] AS [ID], [Project2].[DocumentID] AS [DocumentID], [Project2].[Status] AS [Status], [Project2].[DateCreated] AS [DateCreated] FROM (SELECT [Extent2].[ID] AS [ID], [Extent2].[DocumentID] AS [DocumentID], [Extent2].[Status] AS [Status], [Extent2].[DateCreated] AS [DateCreated] FROM [dbo].[DocumentStatusLogs] AS [Extent2] WHERE ([Distinct1].[DocumentID] = [Extent2].[DocumentID]) ) AS [Project2] ORDER BY [Project2].[ID] DESC) AS [Limit1]
지금은 완전히 원래의 질문에 지정되지 않은 무언가를 있으리라 믿고있어,하지만 테이블 디자인은 경우 귀하의 ID 열이 자동 증가 ID 것을, 그리고 DateCreated은 그렇다하더라도, 각 삽입에 현재 날짜로 설정 당신이 실제로 (반 실행 시간에 대해) GBN의 솔루션에 상당한 성능 향상을 얻을 수 이상이 동일한 정렬 순서를 제공하고 일종의 빠른입니다으로 DateCreated에 ID 대신 주문에 바로 주문에서 내 쿼리를 실행하지 않고.
-
==============================
8.이것은 내가에 현대적인 답을주고 싶어, 그래서 주제에 가장 쉽게 찾을 질문 중 하나입니다 (내 참조를 위해 도움의 다른 출력에 모두)가 그것. FIRST_VALUE 이상 사용하여 당신은 위의 쿼리의 짧은 작품을 만들 수 있습니다 :
이것은 내가에 현대적인 답을주고 싶어, 그래서 주제에 가장 쉽게 찾을 질문 중 하나입니다 (내 참조를 위해 도움의 다른 출력에 모두)가 그것. FIRST_VALUE 이상 사용하여 당신은 위의 쿼리의 짧은 작품을 만들 수 있습니다 :
Select distinct DocumentID , first_value(status) over (partition by DocumentID order by DateCreated Desc) as Status , first_value(DateCreated) over (partition by DocumentID order by DateCreated Desc) as DateCreated From DocumentStatusLogs
이는 SQL Server 2008 및 최대에서 작동합니다. FIRST_VALUE는 이상 절을 사용할 때 선택 톱 1을 달성하는 방법으로 생각 될 수있다. 이상 선택 목록에서 그룹화 그래서 대신 (이렇게 기존의 답변의 많은처럼) 중첩 된 하위 쿼리를 작성 허용, 이것은 더 읽기 방식으로 그것을 않습니다. 도움이 되었기를 바랍니다.
-
==============================
9.내 코드는 각 그룹에서 상위 1을 선택합니다
내 코드는 각 그룹에서 상위 1을 선택합니다
select a.* from #DocumentStatusLogs a where datecreated in( select top 1 datecreated from #DocumentStatusLogs b where a.documentid = b.documentid order by datecreated desc )
-
==============================
10.위에서 클린트의 멋진 및 정답 확인 :
위에서 클린트의 멋진 및 정답 확인 :
두 쿼리 사이의 성능은 아래 흥미 롭다. 52 %는 상부에 존재 하나. 48 %로 두 번째 인. ORDER BY 대신에 DISTINCT를 사용하여 성능에서 4 % 향상. 그러나 ORDER BY 여러 열을 기준으로 정렬 할 수있는 장점이있다.
IF (OBJECT_ID('tempdb..#DocumentStatusLogs') IS NOT NULL) BEGIN DROP TABLE #DocumentStatusLogs END CREATE TABLE #DocumentStatusLogs ( [ID] int NOT NULL, [DocumentID] int NOT NULL, [Status] varchar(20), [DateCreated] datetime ) INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (2, 1, 'S1', '7/29/2011 1:00:00') INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (3, 1, 'S2', '7/30/2011 2:00:00') INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (6, 1, 'S1', '8/02/2011 3:00:00') INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (1, 2, 'S1', '7/28/2011 4:00:00') INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (4, 2, 'S2', '7/30/2011 5:00:00') INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (5, 2, 'S3', '8/01/2011 6:00:00') INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (6, 3, 'S1', '8/02/2011 7:00:00')
옵션 1:
SELECT [Extent1].[ID], [Extent1].[DocumentID], [Extent1].[Status], [Extent1].[DateCreated] FROM #DocumentStatusLogs AS [Extent1] OUTER APPLY ( SELECT TOP 1 [Extent2].[ID], [Extent2].[DocumentID], [Extent2].[Status], [Extent2].[DateCreated] FROM #DocumentStatusLogs AS [Extent2] WHERE [Extent1].[DocumentID] = [Extent2].[DocumentID] ORDER BY [Extent2].[DateCreated] DESC, [Extent2].[ID] DESC ) AS [Project2] WHERE ([Project2].[ID] IS NULL OR [Project2].[ID] = [Extent1].[ID])
옵션 2 :
SELECT [Limit1].[DocumentID] AS [ID], [Limit1].[DocumentID] AS [DocumentID], [Limit1].[Status] AS [Status], [Limit1].[DateCreated] AS [DateCreated] FROM ( SELECT DISTINCT [Extent1].[DocumentID] AS [DocumentID] FROM #DocumentStatusLogs AS [Extent1] ) AS [Distinct1] OUTER APPLY ( SELECT TOP (1) [Project2].[ID] AS [ID], [Project2].[DocumentID] AS [DocumentID], [Project2].[Status] AS [Status], [Project2].[DateCreated] AS [DateCreated] FROM ( SELECT [Extent2].[ID] AS [ID], [Extent2].[DocumentID] AS [DocumentID], [Extent2].[Status] AS [Status], [Extent2].[DateCreated] AS [DateCreated] FROM #DocumentStatusLogs AS [Extent2] WHERE [Distinct1].[DocumentID] = [Extent2].[DocumentID] ) AS [Project2] ORDER BY [Project2].[ID] DESC ) AS [Limit1]
M $의 관리 Studio : -> [디스플레이 예상 실행 계획] 강조 표시하고 첫 번째 블록을 실행 한 후, 모두 옵션 1, 옵션 2, 오른쪽 클릭을 강조 표시합니다. 그런 다음 결과를 볼 수있는 모든 일을 실행합니다.
옵션 1 개 결과 :
ID DocumentID Status DateCreated 6 1 S1 8/2/11 3:00 5 2 S3 8/1/11 6:00 6 3 S1 8/2/11 7:00
옵션 2 개 결과 :
ID DocumentID Status DateCreated 6 1 S1 8/2/11 3:00 5 2 S3 8/1/11 6:00 6 3 S1 8/2/11 7:00
노트 :
또한 피할 내가 끔찍한 실행 계획을 일으키는 원인이 경험 한, WHERE 또는 ON 절에 서브 쿼리 IN / 존재한다. 그러나 주행 거리가 달라집니다. 실행 계획 및 프로필 성능과 필요를 검토!
-
==============================
11.
SELECT o.* FROM `DocumentStatusLogs` o LEFT JOIN `DocumentStatusLogs` b ON o.DocumentID = b.DocumentID AND o.DateCreated < b.DateCreated WHERE b.DocumentID is NULL ;
당신이 만든 날짜 만 최근 문서 순서를 반환 할 경우, 문서 ID 만 상위 1 문서를 반환합니다
-
==============================
12.
SELECT doc_id,status,date_created FROM ( SELECT a.*,Row_Number() OVER(PARTITION BY doc_id ORDER BY date_created DESC ) AS rnk FROM doc a) WHERE rnk=1;
-
==============================
13.여기 쿼리의 각 색인의 최고의 선택과 함께 손에 문제에 대한 3 개 별도의 접근 방식 (인덱스의 자신을 시도하고 논리적 읽기, 경과 시간, 실행 계획을 참조하십시오. 나는 내 경험에서 제안을 제공하고 있습니다 이 특정 문제에 대한 실행하지 않고 이러한 쿼리).
여기 쿼리의 각 색인의 최고의 선택과 함께 손에 문제에 대한 3 개 별도의 접근 방식 (인덱스의 자신을 시도하고 논리적 읽기, 경과 시간, 실행 계획을 참조하십시오. 나는 내 경험에서 제안을 제공하고 있습니다 이 특정 문제에 대한 실행하지 않고 이러한 쿼리).
접근 1 : ROW_NUMBER () 사용. rowstore 지수는 성능을 향상 할 수없는 경우, 당신은 집계 및 그룹화와 쿼리로 서로 다른 열에 의해 모든 시간을 정렬 테이블에 클러스터되지 않은 / 클러스터 columnstore 인덱스를 시도 할 수 columnstore 지수는 일반적으로 최선의 선택이다.
;WITH CTE AS ( SELECT *, RN = ROW_NUMBER() OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC) FROM DocumentStatusLogs ) SELECT ID ,DocumentID ,Status ,DateCreated FROM CTE WHERE RN = 1;
접근법 2 : FIRST_VALUE 사용. rowstore 지수는 성능을 향상 할 수없는 경우, 당신은 집계 및 그룹화와 쿼리로 서로 다른 열에 의해 모든 시간을 정렬 테이블에 클러스터되지 않은 / 클러스터 columnstore 인덱스를 시도 할 수 columnstore 지수는 일반적으로 최선의 선택이다.
SELECT DISTINCT ID = FIRST_VALUE(ID) OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC) ,DocumentID ,Status = FIRST_VALUE(Status) OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC) ,DateCreated = FIRST_VALUE(DateCreated) OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC) FROM DocumentStatusLogs;
접근 방법 3 : CROSS APPLY가 사용. 쿼리에 사용되는 열을 포함 DocumentStatusLogs 테이블에 rowstore 인덱스를 생성하기 columnstore 인덱스의 필요없이 쿼리를 커버하기에 충분합니다.
SELECT DISTINCT ID = CA.ID ,DocumentID = D.DocumentID ,Status = CA.Status ,DateCreated = CA.DateCreated FROM DocumentStatusLogs D CROSS APPLY ( SELECT TOP 1 I.* FROM DocumentStatusLogs I WHERE I.DocumentID = D.DocumentID ORDER BY I.DateCreated DESC ) CA;
-
==============================
14.난 그냥 나 클라이언트에 대한 문제를 해결할 수있는 새로운 오늘 뭔가를 배웠습니다 ....이 게시물을 가로 질러 와서 당신이 만드는 공헌에 대한 여러분 모두 감사드립니다.
난 그냥 나 클라이언트에 대한 문제를 해결할 수있는 새로운 오늘 뭔가를 배웠습니다 ....이 게시물을 가로 질러 와서 당신이 만드는 공헌에 대한 여러분 모두 감사드립니다.
CROSS은 나를 위해 일한으로 나는, 내 솔루션에 사용되는 방법이고, 내 고객의 요구에 적용됩니다. 그리고 내가 읽은 것을 자신의 데이터베이스가 실질적으로 증가 할 전망 전반적으로 최상의 성능을 제공한다에서.
다시 한 번 감사드립니다
-
==============================
15.당신이) (ROW_COUNT를 사용하지 않도록하려는 시나리오에서, 당신은 또한 조인 왼쪽을 사용할 수 있습니다 :
당신이) (ROW_COUNT를 사용하지 않도록하려는 시나리오에서, 당신은 또한 조인 왼쪽을 사용할 수 있습니다 :
select ds.DocumentID, ds.Status, ds.DateCreated from DocumentStatusLogs ds left join DocumentStatusLogs filter ON ds.DocumentID = filter.DocumentID -- Match any row that has another row that was created after it. AND ds.DateCreated < filter.DateCreated -- then filter out any rows that matched where filter.DocumentID is null
예 스키마를 들어, 당신은 또한 일반적으로 왼쪽과 같은 출력을 컴파일하는은 "하지 하위 쿼리에"사용할 수 있습니다 가입 :
select ds.DocumentID, ds.Status, ds.DateCreated from DocumentStatusLogs ds WHERE ds.ID NOT IN ( SELECT filter.ID FROM DocumentStatusLogs filter WHERE ds.DocumentID = filter.DocumentID AND ds.DateCreated < filter.DateCreated)
참고, 서브 쿼리 패턴 작업 테이블이 경우, 기본 키 "ID"를 적어도 하나의 단일 열 고유 키 / 제약 / 인덱스가 없었하지 않는다면.
이러한 쿼리는 모두 (쿼리 분석기로 측정)을 ROW_COUNT () 쿼리보다 "비싼"경향이있다. 그들은 빠른 결과를 반환하거나 다른 최적화를 실행 어디 그러나 시나리오가 발생할 수 있습니다.
-
==============================
16.
SELECT documentid, status, datecreated FROM documentstatuslogs dlogs WHERE status = (SELECT status FROM documentstatuslogs WHERE documentid = dlogs.documentid ORDER BY datecreated DESC LIMIT 1)
-
==============================
17.이 시도:
이 시도:
SELECT [DocumentID] ,[tmpRez].value('/x[2]', 'varchar(20)') AS [Status] ,[tmpRez].value('/x[3]', 'datetime') AS [DateCreated] FROM ( SELECT [DocumentID] ,cast('<x>' + max(cast([ID] AS VARCHAR(10)) + '</x><x>' + [Status] + '</x><x>' + cast([DateCreated] AS VARCHAR(20))) + '</x>' AS XML) AS [tmpRez] FROM DocumentStatusLogs GROUP BY DocumentID ) AS [tmpQry]
-
==============================
18.이 TSQL 내가 가지고 올 수있는 가장 바닐라이다
이 TSQL 내가 가지고 올 수있는 가장 바닐라이다
SELECT * FROM DocumentStatusLogs D1 JOIN ( SELECT DocumentID,MAX(DateCreated) AS MaxDate FROM DocumentStatusLogs GROUP BY DocumentID ) D2 ON D2.DocumentID=D1.DocumentID AND D2.MaxDate=D1.DateCreated
-
==============================
19.당신이 GROUP BY와 함께 다음과 같은 간단한 쿼리를 사용할 수있는 SQLite는 체크 인
당신이 GROUP BY와 함께 다음과 같은 간단한 쿼리를 사용할 수있는 SQLite는 체크 인
SELECT MAX(DateCreated), * FROM DocumentStatusLogs GROUP BY DocumentID
여기 MAX 도움이 각 그룹에서 DateCreated 최대를 얻을 수 있습니다.
그러나 MYSQL이되지 동료 *이 최대 DateCreated의 값으로 -columns 않는 것 같습니다 :(
from https://stackoverflow.com/questions/6841605/get-top-1-row-of-each-group by cc-by-sa and MIT license
'SQL' 카테고리의 다른 글
[SQL] 날짜 범위에서 일을 생성 (0) | 2020.03.06 |
---|---|
[SQL] 어떻게 준비 문은 SQL 주입 공격으로부터 보호 할 수 있습니까? (0) | 2020.03.06 |
[SQL] 카운트 대 (*) COUNT (1) - SQL 서버 (0) | 2020.03.06 |
[SQL] 조인 가입 및 다른 유형의 SQL 지 (0) | 2020.03.06 |
[SQL] A는 관계를 통해-많은이에 SQL 결과를 필터링하는 방법 (0) | 2020.03.06 |