2015-04-16 3 views
6

Mam tabeli z taką strukturąUproszczenie SQL SELECT

CREATE TABLE UsersHistory 
(
    Id   INT IDENTITY, 
    UserID  INT, 
    StatusId INT, 
    CreateTime DATETIME, 
    ChangedTime DATETIME 
) 

INSERT INTO UsersHistory(UserID, StatusId, CreateTime, ChangedTime) 
SELECT 1,1,'20150414','20150414' UNION ALL 
SELECT 1,2,'20150414','20150415' UNION ALL 
SELECT 1,3,'20150414','20150416' UNION ALL 
SELECT 2,1,'20150413','20150413' UNION ALL 
SELECT 2,3,'20150413','20150416' 

i zapytania

;WITH k AS (
SELECT uh.UserID,MAX(uh.ChangedTime) AS Dt FROM UsersHistory AS uh 
WHERE uh.ChangedTime<'20150416' 
GROUP BY uh.UserID 
) 
SELECT k.UserID,uh.StatusId FROM k 
INNER JOIN UsersHistory AS uh 
    ON k.UserID = uh.UserID AND k.Dt = uh.ChangedTime 

Zapytanie jest zbyt łatwe i nie wymaga dalszych wyjaśnień. Chcę to uprościć. (Usuń sprzężenie z kolumną typu daty i czasu).

Wszelkie sugestie?

Odpowiedz

1

Można użyć ROW_NUMBER() z PARTITION do osiągnięcia tego celu. Coś podobnego do tego:

;WITH CTE as 
( 
    SELECT UserID, StatusId, CreateTime, ChangedTime,ROW_NUMBER()OVER(PARTITION BY UserID ORDER BY ChangedTime DESC) r 
    FROM UsersHistory 
    WHERE ChangedTime < '20150416' 
) 
SELECT UserID, StatusId FROM CTE 
WHERE r = 1 
+1

Jest to oczywiście wygrywa. Niezwykle skomplikowane pytania prowadzą czasami do nieoczekiwanie skomplikowanych odpowiedzi. W takich przypadkach dobrze jest widzieć prostotę. –

0

W SQL Server 2012+, można użyć first_value():

SELECT uh.UserID, 
     FIRST_VALUE(uh.StatusId) OVER (PARTITION BY uh.UserId ORDER BY ChangedTIme DESC) 
     MAX(uh.ChangedTime) AS Dt 
FROM UsersHistory AS uh 
WHERE uh.ChangedTime < '20150416'; 
0

Jak rozumiem chcesz wybrać identyfikator użytkownika i StatusId zapisów z maksymalną ChangedTime gdzie ChangedTime nie równa się „20150416”. Proszę, spróbuj to zapytanie:

SELECT 
    uh.UserId, uh.StatusId 
    FROM UsersHistory AS uh 
    WHERE uh.ChangedTime<'20150416' 
    AND uh.ChangedTime=(SELECT MAX(ChangedTime) FROM UsersHistory 
              WHERE UserId=uh.UserId); 
0

Możesz dodać podzapytanie w klauzuli WHERE. Coś takiego:

SELECT uh.UserID, 
     MAX(uh.ChangedTime) AS Dt, 
     uh.StatusId 
FROM UsersHistory uh 
WHERE uh.ChangedTime < '20150416' 
     AND uh.ChangedTime= 
         (
         SELECT MAX(ChangedTime) 
         FROM UsersHistory 
         WHERE UserId=uh.UserId 
         ) 
GROUP BY uh.UserID, uh.StatusId 
+0

Grupujesz według identyfikatora użytkownika i wybierasz statusideout whitout aggregation. To błąd składni. –

+0

Dziękuję za korektę, spudłużyłem, naprawiono. –

0

Z APPLY:

Select uh1.UserID, oa.StatusID 
From UserHistory uh1 
Cross Apply(Select * From (Select Top 1 uh2.StatusID, uh2.Changetime 
      From UserHistory uh2 
      Where uh2.UserID = uh1.UserID And uh2.Changetime < '20150416' 
      Order By uh2.Changetime desc) t where t.Changetime = uh1.Changetime) oa 
0
SELECT 
    distinct uh.UserID, 
    statusid 
FROM UsersHistory uh 
inner join 
    (select 
     UserID, 
     max(ChangedTime) dt 
    from UsersHistory 
    group by UserID) ct 
on (ct.UserID=uh.userid and ct.dt=uh.ChangedTime)