2012-02-04 3 views
1

Учитывая эти таблицы:SQL Query, который вычисляет время

Item 
----- 
Id 
Description 
Status 
CreatedBy 

ItemLog 
-------- 
Id 
ItemId 
NewStatus 
TimeStamp 
ChangedBy 

Где Itemlog.ItemId = Item.Id и Status = { "Created", "Pended", "Cancelled", "Completed" } ...

Как бы вы написать SQL запрос, чтобы генерировать следующие результаты:

Item  Description  ChangeDate  NewStatus  ChangedBy 
    1   Test1   2012-01-01 Created User1 
    1   Test1   2012-01-02 Pended  User2 
    1   Test1   2012-01-03 Completed User2 
    2   Test2   2012-01-01 Created User2 
    2   Test2   2012-01-02 Pended  User3 
    2   Test2   2012-01-09 Cancelled User1 
    3   Test3   2012-01-01 Created User1 
    3   Test3   2012-01-02 Pended  User1 

Item CurrentUser CurrentStatus CreatedOn  TotalTime TimePended CompletedDate CancelledDate 
Test1 User2  Completed  2012-01-01  3 days  1 day  2012-01-03 (null) 
Test2 User1  Completed  2012-01-01  9 days  7 days  (null)  2012-01-09 
Test3 User1  Pended  2012-01-01  35 days  34 days (null)  (null) 

Который я хочу отобразить как отчет master-detail в своем приложении.

Первый ResultSet простой запрос с парой объединений (я не включал UserId-UserName таблицы и т.д.)

Твердая часть расчет общего времени ...

TimeToComplete является TimeStamp последних ItemLog минус TimeStamp первого ItemLog (где ItemLog упорядочивается по дате).

TimePended - это сумма разницы в TimeStamps между каждым изменением статуса из-за ошибки -> что-то.

+0

Ну, я только что закончил то, что вычисляет поле TotalTime, получая МИН TimeStamp, а Мах TimeStamp, группируя их по Id, и затем подставляя их друг из друга. Я не могу думать, как делать PendedTime без использования курсора. – grimstoner

+0

Что такое СУБД? – eaolson

+0

Microsoft Sql Server 2008 R2 – grimstoner

ответ

2

Shazam!

create table Item(ID int, Description varchar(200),createdby varchar(20)) 
create table ItemLog(ID int, ItemID int, NewStatus varchar(200), [TimeStamp] datetime, ChangedBy varchar(20)) 

insert into Item(ID,Description,CreatedBy) values(1, 'Test 1', 'User1') 
insert into Item(ID,Description,CreatedBy) values(2, 'Test 2', 'User1') 
insert into Item(ID,Description,CreatedBy) values(3, 'Test 3', 'User1') 

insert into ItemLog(ID, ItemID, NewStatus, TimeStamp, ChangedBy) 
    values(1,1,'Created','1/1/2012','User1') 
insert into ItemLog(ID, ItemID, NewStatus, TimeStamp, ChangedBy) 
    values(2,1,'Pended','1/2/2012','User2') 
insert into ItemLog(ID, ItemID, NewStatus, TimeStamp, ChangedBy) 
    values(3,1,'Completed','1/3/2012','User2') 
insert into ItemLog(ID, ItemID, NewStatus, TimeStamp, ChangedBy) 
    values(4,2,'Created','1/1/2012','User2') 
insert into ItemLog(ID, ItemID, NewStatus, TimeStamp, ChangedBy) 
    values(5,2,'Pended','1/2/2012','User3') 
insert into ItemLog(ID, ItemID, NewStatus, TimeStamp, ChangedBy) 
    values(6,3,'Cancelled','1/9/2012','User1') 
insert into ItemLog(ID, ItemID, NewStatus, TimeStamp, ChangedBy) 
    values(7,3,'Created','1/2/2012','User1') 
insert into ItemLog(ID, ItemID, NewStatus, TimeStamp, ChangedBy) 
    values(8,3,'Pended','1/2/2012','User1') 

select * from item i left outer join itemlog il on il.ItemID = i.ID 

select 
    i.Description, 
    FirstStatusDate=minIL.TimeStamp, 
    CurrentStatus=maxIL.NewStatus, 
    CurrentStatusDate=maxIL.TimeStamp, 
    CurrentUser=maxIL.ChangedBy, 
    CompletedDate=(select max(TimeStamp) from ItemLog where ItemID=i.ID and NewStatus='Completed'), 
    [TotalTime (in days)]=case 
     when 
      minIL.TimeStamp is not null and maxIL.TimeStamp is not null 
      then datediff(day,minIL.TimeStamp,maxIL.TimeStamp) 
     else 
      convert(int,null) 
     end, 
    TimePending=sum(c.Days) 
from 
    Item i 
    left outer join 
    (
     select 
      y.ItemID, 
      y.MinDate,MinItemLogID=min(mn.id), 
      y.MaxDate,MaxItemLogID=max(mx.id) 
     from 
      (
       select 
        ItemID,MinDate=min(timestamp), MaxDate=max(timestamp) 
       from 
        ItemLog il group by ItemID 
      ) as y 
      left outer join ItemLog mn on mn.ItemID=y.ItemID and mn.TimeStamp=y.MinDate 
      left outer join ItemLog mx on mx.ItemID=y.ItemID and mx.TimeStamp=y.MaxDate 
     group by 
      y.ItemID, y.MinDate, y.MaxDate 
    ) 
    z on z.ItemID = i.ID 
    left outer join ItemLog minIL on minIL.ID = z.MinItemLogID 
    left outer join ItemLog maxIL on maxIL.ID = z.MaxItemLogID 
    left outer join 
    (
     select 
      p.ItemId, 
      PendTime=p.TimeStamp, 
      PendID=p.ID, 
      Days=datediff(day,p.TimeStamp, 
       coalesce(
        (select min(TimeStamp) 
        from 
         ItemLog b 
        where 
         b.ItemID = P.ItemID and TimeStamp > p.TimeStamp) 
        , 
        getdate() 
       ) 
      ) 
     from 
      ItemLog p 
     where 
      p.NewStatus='Pended' 
    ) c on c.ItemID = i.ID 
group by 
    i.ID, 
    i.Description, 
    minIL.TimeStamp, 
    maxIL.NewStatus, 
    maxIL.TimeStamp, 
    maxIL.ChangedBy 

enter image description here

0

Geez ... Значит ли это обеспечить большую выгоду в производительности по сравнению что-то вроде следующего?

ALTER FUNCTION [dbo].[GetItemPendedTime] 
(
    -- Add the parameters for the function here 
    @ItemId uniqueidentifier 
) 
RETURNS INT 
AS 
BEGIN 
    -- Declare the return variable here 
    DECLARE @status INT 
    DECLARE @timespan datetime 
    DECLARE @pendTimeMinutes INT = 0 
    DECLARE @ispend bit = 0 
    DECLARE @lastTimespan datetime 

    DECLARE itemlog_queue CURSOR FOR 
     SELECT Status, Timestamp FROM ItemLog WHERE ItemId = @ItemId 

    OPEN itemlog_queue; 
    FETCH NEXT FROM itemlog_queue INTO @status, @timespan 

    WHILE @@FETCH_STATUS = 0 
    BEGIN  
     IF @ispend = 1 
      BEGIN 
       SET @pendTimeMinutes += DATEDIFF(minute, @lastTimespan, @timespan) 
      END 
     IF @status = 13 
      BEGIN 
       SET @ispend = 1 
      END 
     ELSE 
      BEGIN 
       SET @ispend = 0   
      END 
     SET @lastTimespan = @timespan 
     FETCH NEXT FROM itemlog_queue INTO @status, @timespan 
    END 

    CLOSE itemlog_queue 
    DEALLOCATE itemlog_queue 

    IF @ispend = 1 
    BEGIN 
     SET @pendTimeMinutes = @pendTimeMinutes + DATEDIFF(minute, @lastTimespan, GETDATE()) 
    END 

    -- Return the result of the function 
    RETURN @pendTimeMinutes 

END 

А потом называют это как

SELECT Description, Status as CurrentStatus, dbo.GetItemPendedTime(ItemId) FROM Items 
+0

Я думаю, вы можете увидеть большой удар производительности, используя функцию, которую вы предложили. Когда функция выполняет свои собственные операторы выбора, а затем вызывает ее в контексте другого оператора select, вы заканчиваете работу операторами N SELECT, где N - количество строк во внешнем select (таблица Items). Это может быть катастрофическим в таблице из 10 миллионов строк! Всегда старайтесь использовать теорию множеств: http://blogs.msdn.com/b/sqlprogrammability/archive/2008/03/18/increase-your-sql-server-performance-by-replacing-cursors-with-set-operations .aspx). – sisdog

Смежные вопросы