마지막으로 값이 변경된 것을 찾으려고 시도


26

ID, 값 및 날짜가있는 테이블이 있습니다. 이 표에는 많은 ID, 값 및 날짜가 있습니다.

레코드는이 테이블에 주기적으로 삽입됩니다. ID는 항상 동일하지만 때때로 값이 변경됩니다.

ID와 최근에 값이 변경된 시간을 알려주는 쿼리를 작성하려면 어떻게해야합니까? 참고 : 값이 항상 증가합니다.

이 샘플 데이터에서 :

  Create Table Taco
 (  Taco_ID int,
    Taco_value int,
    Taco_date datetime)

Insert INTO Taco 
Values (1, 1, '2012-07-01 00:00:01'),
        (1, 1, '2012-07-01 00:00:02'),
        (1, 1, '2012-07-01 00:00:03'),
        (1, 1, '2012-07-01 00:00:04'),
        (1, 2, '2012-07-01 00:00:05'),
        (1, 2, '2012-07-01 00:00:06'),
        (1, 2, '2012-07-01 00:00:07'),
        (1, 2, '2012-07-01 00:00:08')

결과는 다음과 같아야합니다.

Taco_ID      Taco_date
1            2012-07-01 00:00:05

(00:05가 마지막으로 Taco_Value변경 되었기 때문에 )


2
나는 taco음식과 아무 관련이 없다고 생각 합니까?
커밋

5
배가 고파서 타코를 먹고 싶습니다. 샘플 테이블의 이름이 필요했습니다.
SqlSandwiches 2016 년

8
비슷한 방식으로 사용자 이름을 선택 했습니까?
Martin Smith

1
가능합니다.
SqlSandwiches 2016 년

답변:


13

이 두 쿼리는 Taco_value시간이 지남에 따라 항상 증가 한다는 가정에 의존합니다 .

;WITH x AS
(
  SELECT Taco_ID, Taco_date,
    dr = ROW_NUMBER() OVER (PARTITION BY Taco_ID, Taco_Value ORDER BY Taco_date),
    qr = ROW_NUMBER() OVER (PARTITION BY Taco_ID ORDER BY Taco_date)
  FROM dbo.Taco
), y AS
(
  SELECT Taco_ID, Taco_date,
    rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID, dr ORDER BY qr DESC)
  FROM x WHERE dr = 1
)
SELECT Taco_ID, Taco_date
FROM y 
WHERE rn = 1;

더 적은 창 기능 광기를 가진 대안 :

;WITH x AS
(
  SELECT Taco_ID, Taco_value, Taco_date = MIN(Taco_date)
  FROM dbo.Taco
  GROUP BY Taco_ID, Taco_value
), y AS
(
  SELECT Taco_ID, Taco_date, 
    rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID ORDER BY Taco_date DESC)
  FROM x
)
SELECT Taco_ID, Taco_date FROM y WHERE rn = 1;

SQLfiddle의


최신 정보

계속 추적하는 사람들에게는 Taco_value반복 될 수 있는 일에 대한 논쟁이있었습니다 . 주어진에 대해 1에서 2로 갔다가 다시 1로 갈 수 있으면 Taco_ID쿼리가 작동하지 않습니다. Itzik Ben-Gan과 같은 누군가가 꿈꿀 수있는 격차 및 섬 기술이 아니더라도 OP의 시나리오와 관련이없는 경우에도 그 경우에 대한 해결책이 있습니다. 미래 독자와 관련이 있습니다. 좀 더 복잡하고 변수 Taco_ID가 하나 더 Taco_value있습니다.

전체 세트에서 값이 전혀 변경되지 않은 ID의 첫 번째 행을 포함하려면 다음을 수행하십시오.

;WITH x AS
(
  SELECT *, rn = ROW_NUMBER() OVER 
    (PARTITION BY Taco_ID ORDER BY Taco_date DESC)
  FROM dbo.Taco
), rest AS (SELECT * FROM x WHERE rn > 1)
SELECT  
  main.Taco_ID, 
  Taco_date = MIN(CASE 
    WHEN main.Taco_value = rest.Taco_value 
    THEN rest.Taco_date ELSE main.Taco_date 
  END)
FROM x AS main LEFT OUTER JOIN rest
ON main.Taco_ID = rest.Taco_ID AND rest.rn > 1
WHERE main.rn = 1
AND NOT EXISTS 
(
  SELECT 1 FROM rest AS rest2
   WHERE Taco_ID = rest.Taco_ID
   AND rn < rest.rn
   AND Taco_value <> rest.Taco_value
) 
GROUP BY main.Taco_ID;

해당 행을 제외하려면 조금 더 복잡하지만 여전히 약간의 변경 사항이 있습니다.

;WITH x AS
(
  SELECT *, rn = ROW_NUMBER() OVER 
    (PARTITION BY Taco_ID ORDER BY Taco_date DESC)
  FROM dbo.Taco
), rest AS (SELECT * FROM x WHERE rn > 1)
SELECT 
  main.Taco_ID, 
  Taco_date = MIN(
  CASE 
    WHEN main.Taco_value = rest.Taco_value 
    THEN rest.Taco_date ELSE main.Taco_date 
  END)
FROM x AS main INNER JOIN rest -- ***** change this to INNER JOIN *****
ON main.Taco_ID = rest.Taco_ID AND rest.rn > 1
WHERE main.rn = 1
AND NOT EXISTS
(
  SELECT 1 FROM rest AS rest2
   WHERE Taco_ID = rest.Taco_ID
   AND rn < rest.rn
   AND Taco_value <> rest.Taco_value
)
AND EXISTS -- ***** add this EXISTS clause ***** 
(
  SELECT 1 FROM rest AS rest2
   WHERE Taco_ID = rest.Taco_ID
   AND Taco_value <> rest.Taco_value
)
GROUP BY main.Taco_ID;

업데이트 된 SQLfiddle 예제


OVER에서 중요한 성능 문제가 있음을 발견했지만 몇 번만 사용했으며 잘못 작성했을 수 있습니다. 눈치 채 셨나요?
케네스 피셔

1
@KennethFisher는 특별히 OVER가 아닙니다. 다른 것과 마찬가지로 쿼리 구성은 기본 스키마 / 인덱스에 크게 의존하여 올바르게 작동합니다. 파티션에 대한 over 절은 GROUP BY와 동일한 문제를 겪습니다.
Aaron Bertrand

@KennethFisher는 단 하나의 고립 된 관측에서 광범위하고 포괄적 인 결론을 도출하지 않도록주의하십시오. CTE에 대해 동일한 주장을 봅니다. "이 재귀 적 CTE를 한 번 가지고 있었고 성능이 저하되었으므로 더 이상 CTE를 사용하지 않습니다."
Aaron Bertrand

그래서 내가 물었다. 나는 어떤 식 으로든 말로 충분히 사용하지는 않았지만 몇 번 사용했을 때 CTE로 더 나은 성능을 얻을 수있었습니다. 그래도 계속 연주하겠습니다.
케네스 피셔

@AaronBertrand 나는 value다시 나타날 경우 이것들이 작동하지 않을 것이라고 생각합니다 : Fiddle
ypercubeᵀᴹ

13

기본적으로 이것은 파생 테이블이없는 단일 SELECT로 "축약 된" Taryn의 제안입니다 .

SELECT DISTINCT
  Taco_ID,
  Taco_date = MAX(MIN(Taco_date)) OVER (PARTITION BY Taco_ID)
FROM Taco
GROUP BY
  Taco_ID,
  Taco_value
;

참고 :이 솔루션은 Taco_value증가 할 수 있는 규정을 고려합니다 . 더 정확히 말하면 Taco_value실제로 연결된 답변과 동일한 이전 값으로 다시 변경할 수 없다고 가정합니다 .

쿼리에 대한 SQL Fiddle 데모 : http://sqlfiddle.com/#!3/91368/2


7
우와, 중첩 MAX / MIN. MIND BLOWN +1
Aaron Bertrand

7

둘 다 사용할 수 있어야 min()하고 max()집계 함수가 결과를 얻습니다.

select t1.Taco_ID, MAX(t1.taco_date) Taco_Date
from taco t1
inner join
(
    select MIN(taco_date) taco_date,
        Taco_ID, Taco_value
    from Taco
    group by Taco_ID, Taco_value
) t2
    on t1.Taco_ID = t2.Taco_ID
    and t1.Taco_date = t2.taco_date
group by t1.Taco_Id

데모가 포함 된 SQL Fiddle 참조


5

값이 다시 나타나지 않는다는 가정을 기반으로 한 가지 추가 답변 (기본적으로 @Aaron의 쿼리 2이며 하나의 작은 둥지에 요약되어 있음) :

;WITH x AS
(
  SELECT 
    Taco_ID, Taco_value, 
    Rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID
                            ORDER BY MIN(Taco_date) DESC),
    Taco_date = MIN(Taco_date) 
  FROM dbo.Taco
  GROUP BY Taco_ID, Taco_value
)
SELECT Taco_ID, Taco_value, Taco_date
FROM x 
WHERE Rn = 1 ;

테스트 : SQL-Fiddle


값이 다시 나타날 수있는보다 일반적인 문제에 대한 답변 :

;WITH x AS
(
  SELECT 
    Taco_ID, Taco_value, 
    Rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID
                            ORDER BY MAX(Taco_date) DESC),    
    Taco_date = MAX(Taco_date) 
  FROM dbo.Taco
  GROUP BY Taco_ID, Taco_value
)
SELECT t.Taco_ID, Taco_date = MIN(t.Taco_date)
FROM x
  JOIN dbo.Taco t
    ON  t.Taco_ID = x.Taco_ID
    AND t.Taco_date > x.Taco_date
WHERE x.Rn = 2 
GROUP BY t.Taco_ID ;

(또는를 사용 CROSS APPLY하여 모든 관련 행을 사용하는 value것이 표시됩니다) :

;WITH x AS
(
  SELECT 
    Taco_ID, Taco_value, 
    Rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID
                            ORDER BY MAX(Taco_date) DESC),    
    Taco_date = MAX(Taco_date) 
  FROM dbo.Taco
  GROUP BY Taco_ID, Taco_value
)
SELECT t.*
FROM x
  CROSS APPLY 
  ( SELECT TOP (1) *
    FROM dbo.Taco t
    WHERE t.Taco_ID = x.Taco_ID
      AND t.Taco_date > x.Taco_date
    ORDER BY t.Taco_date
  ) t
WHERE x.Rn = 2 ;

테스트 : SQL-Fiddle-2


더 일반적인 문제에 대한 제안은 변경 사항이없는 ID에는 적용되지 않습니다. 원래 항목에 더미 항목을 추가하여 고정 할 수 있습니다 (예 :) dbo.Taco UNION ALL SELECT DISTINCT Taco_ID, NULL AS Taco_value, '19000101' AS Taco_date.
Andriy M

@AndriyM 알고 있습니다. 나는 "변화"란 2 개 이상의 값으로, 영업 이익이 명확하지 않은있을 때 그들은 결과를 원하는 가정이 (그리고 그것은 : 쓰기에 용이했기 때문에
ypercubeᵀᴹ

2

샘플 구조 및 데이터를 제공하는 FYI +1 내가 요청할 수있는 유일한 것은 해당 데이터에 대한 예상 출력입니다.

편집 : 이것은 나를 미치게 할 것입니다. 이 작업을 수행하는 "간단한"방법이 새로 생겼습니다. 나는 잘못된 해결책을 제거하고 내가 옳다고 믿는 것을 넣었다. 다음은 @bluefeets와 비슷한 솔루션이지만 @AaronBertrand가 제공 한 테스트를 다룹니다.

;WITH TacoMin AS (SELECT Taco_ID, Taco_value, MIN(Taco_date) InitialValueDate
                FROM Taco
                GROUP BY Taco_ID, Taco_value)
SELECT Taco_ID, MAX(InitialValueDate)
FROM TacoMin
GROUP BY Taco_ID

2
OP는 최근 날짜를 요구하지 않고 value변경 시기를 묻습니다 .
ypercubeᵀᴹ

아, 내 실수를 봅니다. 나는 답을 찾았지만 @Aaron과 거의 동일하므로 게시 할 때 아무런 의미가 없습니다.
케네스 피셔

1

지연 값과 리드 값의 차이를 얻는 것이 어떻습니까? 차이가 0이면 변경되지 않았으며 0이 아니고 변경되었습니다. 간단한 쿼리로 수행 할 수 있습니다.

-- example gives the times the value changed in the last 24 hrs
SELECT
    LastUpdated, [DiffValue]
FROM (
  SELECT
      LastUpdated,
      a.AboveBurdenProbe1TempC - coalesce(lag(a.AboveBurdenProbe1TempC) over (order by ProcessHistoryId), 0) as [DiffValue]
  FROM BFProcessHistory a
  WHERE LastUpdated > getdate() - 1
) b
WHERE [DiffValue] <> 0
ORDER BY LastUpdated ASC

lag...분석 기능은 "최근"원래 질문은 SQL 서버 2008 R2에 대한 솔루션을 요구하고있다 SQL 서버 2012에서 소개되었습니다. SQL Server 2008 R2에서는 솔루션이 작동하지 않습니다.
John aka hot2use

-1

다음과 같이 간단 할 수 있습니까?

       SELECT taco_id, MAX(
             CASE 
                 WHEN taco_value <> MAX(taco_value) 
                 THEN taco_date 
                 ELSE null 
             END) AS last_change_date

taco_value가 항상 증가한다고 가정합니까?

추신 : 나는 꽤 초보자이지만, 천천히 배우지 만 확실히 배우십시오.


1
SQL Server에서는 오류가 발생합니다. Cannot perform an aggregate function on an expression containing an aggregate or a subquery
Martin Smith

2
Martin의 의견에 요점 추가 : 테스트 된 코드 만 게시하면 안전합니다. 평소 운동장에서 멀리 떨어져 있다면 쉬운 방법은 sqlfiddle.com 으로 갈 수 있습니다 .
dezso
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.