根据条件使用max的条件选择第二个max作为max

时间:2015-08-11 20:32:37

标签: sql sql-server tsql sql-server-2000

尝试创建报告,以查看存储单元在1个租约和另一个租赁之间空置的时间。信息存储在客户表中,其中包含以下列:

PK (Customer Code) | Storage_Number (FK) | Status | startdate | enddate

我获得了基本代码,用于寻找每个单位的MAX(最后)客户以及之前的1位客户。我的问题是存储单元目前是空置的。它将显示New Customer Start Date过去的客户,而我希望此字段为空,Prior Customer End Date应来自最近的客户。

Select s.PK [Storage Number], 
ca.enddate [Prior Customer End Date],
cb.startdate [New Customer Start Date],
Datediff(DAY, ca.enddate, cb.startdate)-1 [Days Vacant]
From Storage s
Left Outer Join (
    Select Storage_Number, 
    MAX(PK) Customer_Code
    FROM Customer
    WHERE Status in (‘Future’, ‘Active’, ’Past’)
    Group By Storage_Number
)maxa On maxa.Storage_Number = s.PK
Left Outer Join(
    Select Storage_Number,
    MAX(PK) Customer_Code
    FROM Customer b
    Full Outer Join (
        Select Storage_Number sn, 
        MAX(PK) cc
        FROM Customer
        WHERE Status in (‘Future’, ‘Active’, ’Past’)
        Group By Storage_Number
    )a on a.sn = b.Storage_Number
    WHERE Status in (‘Future’, ‘Active’, ’Past’)
    And b.PK != a.cc
    Group by Storage_Number
)maxb
ON maxb.Storage_Number = s.PK
Left Outer Join Customer ca on ca.Storage_Number = s.PK and ca.PK = maxb.PK
Left Outer Join Customer cb on cb.Storage_Number = s.PK and cb.PK = maxa.PK
WHERE ca.enddate !> cb.startdate
Order By ca.enddate

我可以添加多个CASE语句

Select s.PK [Storage Number], 
CASE
    WHEN    cb.status = ‘Past’
    THEN cb.enddate
    Else ca.enddate
END [Prior Customer End Date],
CASE
    WHEN cb.status = ‘Past’
    THEN null
    ELSE cb.startdate
END [New Customer Start Date],
CASE
    WHEN cb.status = ‘Past’
    THEN null 
    ELSE Datediff(DAY, ca.enddate, cb.startdate)-1
END [Days Vacant]
FROM ………..

但是我想看看是否有更好的方法来做到这一点。

1 个答案:

答案 0 :(得分:1)

<强>更新

SQL 2000版本(使用下面的设置代码/我没有经过测试,因为没有SQL 2000实例;希望所有内容都与该版本兼容,因为我没试过使用任何太现代的东西。)

declare @firstAvailableDate datetime = '2014-12-20' --allows us to see how many days the units were available before their first booking
, @lastAvailableDate datetime = getutcdate() --allows us to say up to which date we're interested for the last booking

declare @BookingSequence table
(
    OrderByStorageId bigint identity(1,1)
    , StorageId bigint
    , StartDate datetime
    , EndDate datetime
    , CustomerId bigint 
)

insert @BookingSequence  
select StorageId, StartDate, EndDate, CustomerId
from @CustomerBookings
order by StorageId, StartDate

declare @BookingPrevAndCurrent table
(
    StorageId bigint
    , StartDate datetime
    , EndDate datetime
    , PreviousBookingEndDate datetime
    , CustomerId bigint
    , OrderByStorageId bigint
)

insert @BookingPrevAndCurrent 
select c.StorageId, c.StartDate, c.EndDate, coalesce(p.EndDate,@firstAvailableDate), c.CustomerId, c.OrderByStorageId
from @BookingSequence c
left outer join @BookingSequence p
on p.StorageId = c.StorageId 
and p.OrderByStorageId = c.OrderByStorageId - 1

select s.Name StorageUnit
, c.Name Customer
, x.PreviousBookingEndDate 
, coalesce(x.StartDate, @lastAvailableDate) CurrentBookingStartDate
, DateDiff(Day,x.PreviousBookingEndDate, coalesce(x.StartDate, @lastAvailableDate)) DaysBetweenBookings
from @Storage s
left outer join @BookingPrevAndCurrent  x
    on x.StorageId = s.id
left outer join @Customer c
    on c.id = x.CustomerId
order by StorageUnit, PreviousBookingEndDate

SQL 2008版本

尝试这样的事情:

SQL Fiddle

--setup code / sample data

declare @CustomerBookings table
(
    Id bigint identity(1,1)
    , CustomerId bigint
    , StorageId bigint 
    , StatusId int
    , StartDate datetime
    , EndDate datetime
)
declare @Storage table
(
    id bigint identity(1,1)
    , Name nvarchar(32) not null
)
declare @Customer table
(
    id bigint identity(1,1)
    , Name nvarchar(32) not null
)
insert @Storage values('A'),('B'),('C')
insert @Customer values('Jane'),('Paul'),('Keith')
insert @CustomerBookings 
select c.id, s.id, 1, x.b, x.e
from 
(
    select 'Jane' c, 'A' s, '2015-01-01' b, '2015-01-03' e 
    union all
    select 'Jane' c, 'A' s, '2015-01-05' b, '2015-01-06' e 
    union all
    select 'Jane' c, 'B' s, '2015-01-05' b, '2015-01-06' e 
    union all
    select 'Paul' c, 'C' s, '2015-01-02' b, '2015-01-06' e 
    union all
    select 'Paul' c, 'A' s, '2015-01-07' b, '2015-01-10' e 
    union all
    select 'Paul' c, 'A' s, '2015-01-012' b, '2015-01-18' e 
    union all
    select 'Keith' c, 'B' s, '2015-01-01' b, '2015-01-04' e 
    union all
    select 'Keith' c, 'A' s, '2015-01-20' b, '2015-01-23' e 
    union all
    select 'Keith' c, 'B' s, '2015-01-08' b, '2015-01-10' e 
    union all
    select 'Keith' c, 'B' s, '2015-01-20' b, '2015-01-23' e 
) x
inner join @Storage s on s.Name = x.s
inner join @Customer c on c.Name = x.c

--the actual solution

declare @firstAvailableDate datetime = '2014-12-20' --allows us to see how many days the units were available before their first booking
, @lastAvailableDate datetime = getutcdate() --allows us to say up to which date we're interested for the last booking

;with BookingSequence as 
(
    select StorageId, StartDate, EndDate, CustomerId, ROW_NUMBER() over (partition by StorageId order by StartDate) OrderByStorageId
    from @CustomerBookings
)
, BookingPrevAndCurrent as
(
    select StorageId, StartDate, EndDate, @firstAvailableDate PreviousBookingEndDate, CustomerId, OrderByStorageId
    from BookingSequence c
    where c.OrderByStorageId = 1

    union all

    select c.StorageId, c.StartDate, c.EndDate, p.EndDate, c.CustomerId, c.OrderByStorageId
    from BookingPrevAndCurrent  p
    inner join BookingSequence c
    on c.StorageId = p.StorageId
    and c.OrderByStorageId = p.OrderByStorageId + 1
)
select s.Name StorageUnit
, c.Name Customer
, x.PreviousBookingEndDate 
, coalesce(x.StartDate, @lastAvailableDate) CurrentBookingStartDate
, DateDiff(Day,x.PreviousBookingEndDate, coalesce(x.StartDate, @lastAvailableDate)) DaysBetweenBookings
from @Storage s
left outer join BookingPrevAndCurrent  x
    on x.StorageId = s.id
left outer join @Customer c
    on c.id = x.CustomerId
order by StorageUnit, PreviousBookingEndDate