각 그룹의 상위 1 개 행 가져 오기


527

각 그룹의 최신 항목을 얻고 싶은 테이블이 있습니다. 테이블은 다음과 같습니다.

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   |

테이블은 DocumentIDDateCreated 내림차순 으로 정렬됩니다 . 각각에 대해 DocumentID최신 상태를 원합니다.

내가 선호하는 결과물 :

| DocumentID | Status | DateCreated |
| 1          | S1     | 8/02/2011   |
| 2          | S3     | 8/01/2011   |
| 3          | S1     | 8/02/2011   |
  • 각 그룹에서 최상위를 얻는 집계 함수가 있습니까? 아래 의사 코드를 참조하십시오 GetOnlyTheTop.

    SELECT
      DocumentID,
      GetOnlyTheTop(Status),
      GetOnlyTheTop(DateCreated)
    FROM DocumentStatusLogs
    GROUP BY DocumentID
    ORDER BY DateCreated DESC
  • 이러한 기능이 존재하지 않으면 원하는 출력을 얻을 수있는 방법이 있습니까?

  • 또는 처음에는 정규화되지 않은 데이터베이스로 인해 발생할 수 있습니까? 나는 내가 찾고있는 것이 단지 한 행이기 때문에 생각하고있다.status 때문에 부모 테이블에도 합니다.

자세한 내용은 부모 테이블을 참조하십시오.

현재 Documents테이블

| DocumentID | Title  | Content  | DateCreated |
| 1          | TitleA | ...      | ...         |
| 2          | TitleB | ...      | ...         |
| 3          | TitleC | ...      | ...         |

상태에 쉽게 액세스 할 수 있도록 부모 테이블이 이와 같아야합니까?

| DocumentID | Title  | Content  | DateCreated | CurrentStatus |
| 1          | TitleA | ...      | ...         | s1            |
| 2          | TitleB | ...      | ...         | s3            |
| 3          | TitleC | ...      | ...         | s1            |

업데이트 방금 "적용"을 사용하여 이러한 문제를보다 쉽게 ​​해결하는 방법을 배웠습니다.


2
가능한 솔루션에 대한 자세한 논의 및 비교를 위해 dba.se에서 비슷한 질문을 읽는 것이 좋습니다 . 그룹당 n 행 검색 .
블라디미르 바라 노프

나는 게시물을보고 시도했다. StoreID로 그룹을 사용 하면 오류가 발생했습니다.
UltraJ

답변:


753
;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를 사용하십시오.

정규화 여부는 다음과 같이 원하는지 여부에 따라 다릅니다.

  • 2 곳의 지위를 유지하다
  • 상태 기록 유지
  • ...

그대로, 상태 기록을 유지합니다. 부모 테이블에서도 최신 상태를 원한다면 (비정규 화) 부모에서 "상태"를 유지하기위한 트리거가 필요합니다. 이 상태 기록 테이블을 삭제하십시오.


5
그리고 ... 무엇입니까 Partition By? With나도 처음이다 :( 어쨌든 mssql 2005를 사용하고 있습니다.
dpp

6
@domanokz : Partition By가 카운트를 재설정합니다. 따라서이 경우에는 DocumentID에 따라 계산됩니다
gbn

1
흠, 나는 성능에 대해 걱정하고 수백만 행을 쿼리 할 것입니다. SELECT * FROM (SELECT ...)이 성능에 영향을 줍니까? 또한 ROW_NUMBER각 행에 대해 일종의 하위 쿼리입니까?
dpp

1
@domanokz : 아니요, 하위 쿼리가 아닙니다. 정확한 색인이 있으면 수백만 개가 문제가되지 않습니다. 어쨌든 2 가지 기반 방법이 있습니다 : 이것과 집계 (Ariel의 솔루션). 그럼 둘 다 시도해보십시오.
gbn

1
@domanokz : ORDER BY DateCreated DESC를 ID DESC로 ORDER BY로 변경
gbn

184

방금 사용법을 배웠습니다 cross apply. 이 시나리오에서 사용하는 방법은 다음과 같습니다.

 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

2
문제가 여전히 해결되었으므로 실제로 아무런 차이가 없습니다.
dpp

19
방금 제안 된 모든 솔루션에 대해 타이밍 테스트 결과를 게시했으며 귀하의 솔루션이 맨 위에 나왔습니다. 당신에게 투표하기 :-)
John Fairbanks

3
엄청난 속도 향상을 위해 +1 ROW_NUMBER ()와 같은 윈도우 함수보다 훨씬 빠릅니다. SQL이 쿼리와 같이 ROW_NUMBER () = 1을 인식하고 적용에 최적화하면 좋을 것입니다. 참고 : 결과에 적용되지 않았더라도 결과가 필요할 때 외부 적용을 사용했습니다.
TamusJRoyce

8
@TamusJRoyce 당신은 이것이 항상 사실이라면 더 빠르기 때문에 외삽 할 수 없습니다. 때에 따라 다르지. 여기에 설명 된대로 sqlmag.com/database-development/optimizing-top-n-group-queries
Martin Smith

2
내 의견은 여러 행을 갖는 것에 관한 것이며 그룹 당 여러 행 중 하나만 원합니다. 조인은 일대 다를 원할 때 사용됩니다. 일대 다가 있지만 일대일을 제외한 모든 항목을 필터링하려는 경우에 적용됩니다. 시나리오 : 100 명의 회원을 위해 각각의 전화 번호를 알려주십시오 (여러 번호를 가질 수있는 곳). Apply가 탁월한 곳입니다. 적은 읽기 = 적은 디스크 액세스 = 더 나은 성능. 내 경험에 따르면 제대로 설계되지 않은 비정규 데이터베이스가 있습니다.
TamusJRoyce 2016 년

53

여기에서 다양한 권장 사항에 대한 몇 가지 타이밍을 수행했으며 결과는 실제로 관련된 테이블의 크기에 따라 다르지만 가장 일관된 솔루션은 CROSS APPLY를 사용하는 것입니다. 이러한 테스트는 SQL Server 2008-R2에서 6,500 개의 레코드와 1 억 3,700 만 개의 레코드가있는 다른 (동일한 스키마). 쿼리되는 열은 테이블의 기본 키의 일부이며 테이블 너비는 매우 작습니다 (약 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

정말 놀라운 점은 관련된 행 수에 관계없이 CROSS APPLY의 시간이 얼마나 일관성이 있었는지 생각합니다.


8
모두 데이터 배포 및 사용 가능한 인덱스에 따라 다릅니다. dba.se 에 대해 오랫동안 논의되었습니다 .
블라디미르 바라 노프

48

나는 이것이 오래된 스레드라는 것을 알고 있지만 TOP 1 WITH TIES솔루션은 꽤 좋고 솔루션을 읽는 데 도움이 될 수 있습니다.

select top 1 with ties
   DocumentID
  ,Status
  ,DateCreated
from DocumentStatusLogs
order by row_number() over (partition by DocumentID order by DateCreated desc)

TOP 절에 대한 자세한 내용은 여기를 참조하십시오 .


7
이것은 가장 우아한 솔루션입니다.
George Menoutis

1
동의-이것은 다른 버전의 SQL 및 다른 언어에서 매우 쉬운 작업을 가장 잘 재현합니다.
Chris Umphlett

27

성능이 걱정된다면 MAX ()를 사용하여이 작업을 수행 할 수도 있습니다.

SELECT *
FROM DocumentStatusLogs D
WHERE DateCreated = (SELECT MAX(DateCreated) FROM DocumentStatusLogs WHERE ID = D.ID)

ROW_NUMBER ()에는 SELECT 문에 모든 행이 필요하지만 MAX는 그렇지 않습니다. 쿼리 속도를 크게 향상시켜야합니다.


2
올바른 색인으로 ROW_NUMBER ()의 성능 문제를 해결할 수 없습니까? (어쨌든 그렇게해야한다고 생각합니다)
Kristoffer L

8
날짜 시간을 사용하면 동일한 날짜와 시간에 두 항목이 추가되지 않을 수 있습니다. 정밀도가 충분하지 않습니다.
TamusJRoyce

단순화를 위해 +1 @TamusJRoyce가 옳습니다. 이건 어떤가요? 'select * from DocumentStatusLog D 여기서 ID = (D.DocumentID = DateCreated DESC 제한 1에 의한 DocumentID 순서 인 DocumentsStatusLog에서 ID 선택);'
cibercitizen1

SELECT * FROM EventScheduleTbl D WHERE DatesPicked = (선택 상위 1 분 (DatesPicked) FROM EventScheduleTbl WHERE EventIDf = D.EventIDf 및 DatesPicked> = convert (date, getdate ()))
Arun Prasad ES

row_number()적절한 인덱싱으로도 성능이 우수한 경우가 있습니다. 자체 조인 시나리오에서 특히 유용합니다. 그러나인지해야 할 것은이 방법은 하위 트리 비용이 낮음에도 불구하고 종종 논리적 읽기 및 스캔 수를 더 많이 생성한다는 것입니다. 실제로 더 좋은지 확인하려면 특정 사례의 비용 / 혜택을 측정해야합니다.
pimbrouwers

26
SELECT * FROM
DocumentStatusLogs JOIN (
  SELECT DocumentID, MAX(DateCreated) DateCreated
  FROM DocumentStatusLogs
  GROUP BY DocumentID
  ) max_date USING (DocumentID, DateCreated)

어떤 데이터베이스 서버? 이 코드는 모두 작동하지 않습니다.

귀하의 질문의 후반부에 관해서는 상태를 열로 포함시키는 것이 합리적입니다. DocumentStatusLogs로그로 남겨 둘 수 있지만 여전히 최신 정보를 기본 테이블에 저장하십시오.

BTW, DateCreatedDocuments 테이블에 이미 열 이 있으면 DocumentStatusLogs( DateCreated독특한 한) 열을 사용하여 조인 할 수 있습니다 .DocumentStatusLogs ) .

편집 : MsSQL은 USING을 지원하지 않으므로 다음과 같이 변경하십시오.

ON DocumentStatusLogs.DocumentID = max_date.DocumentID AND DocumentStatusLogs.DateCreated = max_date.DateCreated

5
실마리는 MSSQL이라는 제목이었습니다. SQL Server에는 USING이 없지만 아이디어는 괜찮습니다.
gbn

7
@gbn 바보 같은 중재자는 일반적으로 여기서 한 것처럼 제목에서 중요한 키워드를 삭제합니다. 검색 결과 나 Google에서 정답을 찾기가 매우 어렵습니다.
NickG

2
Jus는이 "솔루션"이max(DateCreated)
MoonKnight

12

이것은 주제에서 가장 쉽게 찾을 수있는 질문 중 하나이므로 그것에 대한 현대적인 답변을 원했습니다 (둘 다 참조하고 다른 사람들을 돕기 위해). 하여 사용 first_value하고 over당신은 위의 쿼리의 짧은 작품을 만들 수 있습니다 :

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달성하는 방법으로 생각할 수 있습니다 . 선택 목록에서 그룹화 할 수 있으므로 기존의 많은 답변과 같이 중첩 된 하위 쿼리를 작성하는 대신보다 읽기 쉬운 방식으로 수행합니다. 도움이 되었기를 바랍니다.Select Top 1overOver


2
SQL Server 2008 R2에서는 작동하지 않습니다. 2012 년에 first_value가 소개 된 것 같습니다.
ufo

1
매우 빠릅니다! @dpp에서 제공하는 Cross Apply 솔루션을 사용하고 있었지만이 솔루션은 더 빠릅니다.
MattSlay

11

이것은 꽤 오래된 스레드이지만 허용 된 답변이 특히 나에게 적합하지 않은 것과 같은 방식으로 2 센트를 던질 것이라고 생각했습니다. 큰 데이터 세트에서 gbn의 솔루션을 사용해 보니 SQL Server 2012의 5 백만 플러스 레코드에서 45 초 이상>이 매우 느립니다. 실행 계획을 보면 문제가 심각하게 느려지는 SORT 작업이 필요하다는 것이 문제입니다.

SORT 작업이 필요없고 비 클러스터 색인 검색을 수행하는 엔티티 프레임 워크에서 해제 한 대안이 있습니다. 이렇게하면 위에서 언급 한 레코드 세트에서 실행 시간이 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가 각 삽입마다 현재 날짜로 설정된 경우 위의 쿼리를 실행하지 않으면 DateCreated 에서 주문하는 대신 ID주문하는 것만으로 gbn의 솔루션 (약 절반의 실행 시간)으로 상당한 성능 향상을 얻을 수 있습니다. 이는 동일한 정렬 순서를 제공하고 더 빠른 정렬입니다.


5

각 그룹에서 최상위 1을 선택하는 코드

#DocumentStatusLogs에서 a. *를 선택하십시오. 
 날짜 생성 날짜 (#DocumentStatusLogs에서 생성 한 상위 1 개 날짜 선택 b
어디 
a.documentid = b. 문서 ID
날짜가 작성된 디스크로 주문
)

3

위에서 Clint의 훌륭하고 정답을 확인하십시오.

아래 두 쿼리 간의 성능은 흥미 롭습니다. 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 $의 Management 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

노트 :

조인을 1 대 1로 만들고 싶을 때 APPLY를 사용하는 경향이 있습니다.

조인을 일대 다 또는 다 대다로 만들고 싶다면 JOIN을 사용합니다.

고급 작업을 수행해야하는 경우가 아니라면 ROW_NUMBER ()로 CTE를 피하고 윈도우 성능 저하에 대해서는 괜찮습니다.

또한 WHERE 또는 ON 절에서 EXISTS / IN 하위 쿼리를 피할 수 있습니다. 이로 인해 끔찍한 실행 계획이 발생했습니다. 그러나 마일리지는 다양합니다. 언제 어디서나 실행 계획 및 프로필 성능을 검토하십시오!


3

이 솔루션은 각 파티션에 대한 TOP N 가장 최근 행을 가져 오는 데 사용할 수 있습니다 (예 : WHERE 문에서 N은 1이고 파티션은 doc_id입니다).

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;

2
SELECT o.*
FROM `DocumentStatusLogs` o                   
  LEFT JOIN `DocumentStatusLogs` b                   
  ON o.DocumentID = b.DocumentID AND o.DateCreated < b.DateCreated
 WHERE b.DocumentID is NULL ;

DateCreated에서 최근 문서 오더 만 반환하려는 경우 DocumentID로 상위 1 개의 문서 만 반환합니다.


2

CROSS APPLY솔루션이 저에게 효과적이며 고객의 요구에 사용 된 방법이었습니다. 그리고 내가 읽은 내용에서 데이터베이스가 크게 성장할 때 최상의 전체 성능을 제공해야합니다.


1

다음은 각각의 쿼리에 대한 최상의 인덱싱 선택과 함께 문제에 대한 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 테이블에서 행 저장소 인덱스를 작성하면 열 저장소 인덱스가 없어도 쿼리를 처리 할 수 ​​있습니다.

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;

1

나는 이것을 이렇게 할 수 있다고 믿는다. 약간의 조정이 필요할 수 있지만 그룹에서 최대 값을 선택할 수 있습니다.

이 답변은 과도합니다 ..

SELECT
  d.DocumentID,
  MAX(d.Status),
  MAX(d1.DateCreated)
FROM DocumentStatusLogs d, DocumentStatusLogs d1
USING(DocumentID)
GROUP BY d.DocumentID
ORDER BY DateCreated DESC

0

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 

예제 스키마의 경우 일반적으로 왼쪽 조인과 동일한 출력으로 컴파일되는 "not in subquery"를 사용할 수도 있습니다.

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 () 쿼리보다 쿼리 비용이 많이 드는 경향이 있습니다 (쿼리 분석기로 측정). 그러나 결과가 더 빨리 반환되거나 다른 최적화가 가능한 시나리오가 발생할 수 있습니다.


0
SELECT documentid, 
       status, 
       datecreated 
FROM   documentstatuslogs dlogs 
WHERE  status = (SELECT status 
                 FROM   documentstatuslogs 
                 WHERE  documentid = dlogs.documentid 
                 ORDER  BY datecreated DESC 
                 LIMIT  1) 

0

이 시도:

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]

항상 SQL 문이 작동하는 방식을 설명하고 OP 쿼리를 해결하는 방법을 설명해야합니다.
Suraj Kumar

-1

이것은 내가 생각해 낼 수있는 가장 바닐라 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

불행히도 MaxDate는 고유하지 않습니다. 같은 시간에 두 개의 날짜를 입력 할 수 있습니다. 따라서 그룹당 중복이 발생할 수 있습니다. 그러나 ID 열 또는 GUID를 사용할 수 있습니다. 신원 열은 가장 최근에 입력 한 것입니다 (기본 신분 계산, 1 ... x 1 단계).
TamusJRoyce

그럼 난 종류의 동의하지만, 저자는 최신 항목에 대한 질문 - 당신은 정확히 같은 시간에 추가 된 두 항목이 동등있는 자동 증가 ID 열 수단을 포함하지 않는 '최신'
의 풍부한

최신 레코드는 하나의 레코드입니다. 네 자동 증분 ID 열을 고려해야합니다.
TamusJRoyce

-2

SQLite에서 GROUP BY 와 함께 다음과 같은 간단한 쿼리를 사용할 수 있는지 확인했습니다.

SELECT MAX(DateCreated), *
FROM DocumentStatusLogs
GROUP BY DocumentID

여기서 MAX는 각 그룹 에서 최대 DateCreated 를 얻는 데 도움이됩니다 .

그러나 MYSQL은 * -columns를 max DateCreated 값과 연결하지 않는 것 같습니다. (

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