SQL-запрос, который вычисляет время - PullRequest
1 голос
/ 04 февраля 2012

С учетом этих таблиц:

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 в своем приложении.

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

Сложная часть - это вычисление общего времени ...

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

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

Ответы [ 2 ]

2 голосов
/ 05 февраля 2012

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 голосов
/ 05 февраля 2012

Боже ... Это дает большой выигрыш в производительности по сравнению с чем-то вроде следующего?

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...