如何查找结果之间的天数

时间:2016-03-08 18:24:52

标签: sql sql-server sql-server-2008 tsql sql-server-2008-r2

我有一个票务明细表,用于存储交易信息。以下是表格数据的示例:

Ticket_Number  Detail_type_ID   Description  Date_Created   TotalAmount   Barcode 
   1                 11         Card Sale      1/1/16           5           123
   1                 1          Book           1/1/16           5            
   1                 11         Card Red       1/1/16          -5           123 
   2                 1          book           1/5/16           5
   3                 1          book           1/6/16           5
   3                 11         Card Red       1/6/16          -5           123
   4                 11         Card Sale      1/7/16           5           124
   5                 1          Book           1/7/16           5
   5                 11         Card Red       1/7/16          -5           124
   6                 11         Card Sale      1/8/16           5           123
   6                 1          Book           1/8/16           5
   6                 11         Card Red       1/8/16          -5           123
   7                 1          Book           1/9/16           5
   7                 11         Card Red       1/9/16          -5           124

我们出售礼品卡 - 5美元允许您购买2本书。礼品卡上装有2本书。您可以在上表中看到,在大多数情况下,我们会出售一张卡片,并立即兑换一本书。客户在某个时候回来,并购买另一本书,卡上剩余的余额。

我们希望了解的是:客户多久会回来并兑换余额,或者他们需要多长时间来消耗卡。正如您所看到的 - 条形码存储在详细信息表中,但是,我们会重新使用这些卡片,因此我们不希望这样会污染第一张卡片的数据。 Detail_Type_ID为' 11'是指卡片销售或兑换。根据上面的数据,这是I' m寻找的输出:

Barcode      Days_between_usage    Balance_still_remains
 123             6                       No
 124             2                       No
 123(2)          0                      Yes

"余额仍然存在"会告诉我这张卡还有余额。

如何运行查询以获取此输出?

编辑:

基于下面的答案,看起来第一步是将数据分解为我已经完成的销售和赎回。我不确定如何从这里开始。

Select barcode, date_created, Case When TotalAmount > 0 Then 'Sale' Else 'Redeem' end as SaleOrRedeem
From Ticketsdetails
Where  (Date_Created > '1/1/16') and (Detail_Type_ID = '11') and (barcode In (select Barcode
From Ticketsdetails as td
where Date_Created > '1/1/16') and (Detail_Type_ID = '11') and Total Amount > 0)))
Order By Barcode, date_Created

返回:

Barcode           Date_Created      TransType
 123                1/1/16           Sale
 123                1/1/16           Redeem
 123                1/6/16           Redeem
 124                1/7/16           Sale
 124                1/7/16           Redeem
 124                1/7/16           Sale
 123                1/8/16           Sale
 123                1/8/16           Redeem
 124                1/9/16           Redeem

3 个答案:

答案 0 :(得分:6)

在黑暗中刺伤。我不确定我是否完全理解你的要求。

with Sales as (
    select
        t.Barcode,
        t.Date_Created as Sale_Date,
        row_number() over (partition by t.Barcode order by t.Sale_Date) as Load_Seq
    from <Transactions> as t
    where Description = 'Card Sale'
    group by Barcode
),
RedemptionWindows as (
    select
        s1.Barcode,
        s1.Load_Seq
        s1.Sale_Date,
        coalesce(s2.Sale_Date, dateadd(year, 1, s1.Sale_Date)) as End_Date,
    from Sales as s1 left outer join Sales s2
        on s2.Barcode = s1.Barcode and s2.Load_Seq = s1.Load_Seq + 1
)
select
    Barcode
      + case 
             when Load_Seq > 1 
             then '(' + cast(Load_Seq as varchar(3)) + ')' 
             else '' end as Barcode,
    Days_Between_Usage,
    case when RedemptionCount < 2 then 'Yes' else 'No' Balance_Still_Remains,
    5.00 - 2.50 * RedemptionCount as Balance_Remaining
from
    RedemptionWindows as rw
    cross apply
    (
        select
            datediff(day,min(r.Date_Created),max(r.Date_Created)) as Days_Between_Usage,
            count(*) as RedemptionCount
        from <Transactions> as r /* redemptions */
        where Description = 'Card Red'
            and r.Barcode = rw.Barcode
            and r.Date_Created >= rw.Sale_Date 
            and r.Date_Created <  rw.End_Date
    ) r_summary

答案 1 :(得分:0)

您可以使用自联接或使用子查询来执行此操作。

这是一些伪代码

{{1}}

然后要获得最后一行,您将使用另一个生成所需行的查询来使用UNION。您可以使用CASE表达式根据剩余余额是否> 1来生成字符串。 0或不。并且可以使用子查询在条形码旁边的括号中生成数字。

答案 2 :(得分:0)

稍微复杂的代码可以为您提供解决方案。

我必须说 - 男人,重新使用条形码,在所呈现的场景中,将迫使你的解决方案始终在整个销售历史中向后看,只是为了设计重复使用事件的顺序&#39;在给定的背景下。

您最好不要重复使用条形码或对“重复使用订单”进行非规范化处理&#39;作为Ticketsdetails上的新列。

...享受

SET NOCOUNT ON
go

--------------------------------------------------------------------------------
-- Isolate the card sale/redeem events (we are not interested in the books)
--
-- note the 'barcode_reuse_count' column
--------------------------------------------------------------------------------

DECLARE @price_per_book MONEY

SET @price_per_book = 5

SELECT
    Ticket_Number, barcode, date_created,
    --
    CASE WHEN Description = 'Card Sale' THEN 1
                                        ELSE 0
    END AS is_event_of_sale /* is this an event of card sale? */,
    --
    CASE WHEN
        Description = 'Card Sale' THEN (TotalAmount * 2) / @price_per_book /* Well, $5 HAS to mean TWO books... */
                                  ELSE TotalAmount / @price_per_book
    END AS Credit_Or_Debit_As_Books,
    --
    CONVERT(INT, NULL) AS barcode_reuse_order
INTO #card_events
FROM Ticketsdetails
WHERE Detail_Type_ID = '11'
go

--------------------------------------------------------------------------------
-- For each ticket, identify the ticket where the card in its 'barcode reuse'
-- incarnation was sold (although there should be one, and only one, barcode per
-- Ticket_Number, the query allows for more than one...)
--
-- Then, set 'barcode_reuse_order'
--------------------------------------------------------------------------------

WITH card_sale_event AS
(
    SELECT card_event.Ticket_Number, card_event.barcode, MAX(card_event_sale.Ticket_Number) AS Ticket_Number_Of_Sale
    FROM
        #card_events card_event
            INNER JOIN #card_events card_event_sale
            ON(
                    card_event_sale.barcode          =  card_event.barcode
                AND card_event_sale.is_event_of_sale =  1
                AND card_event_sale.Ticket_Number    <= card_event.Ticket_Number
            )
    GROUP BY card_event.Ticket_Number, card_event.barcode
)
UPDATE card_event
SET
    barcode_reuse_order =(
        SELECT
            COUNT(DISTINCT prev_card_sale_event.Ticket_Number_Of_Sale)
            /* card_event_sale may have many Ticket_Number_Of_Sale per barcode, one per Ticket_Number */
        FROM card_sale_event prev_card_sale_event
        WHERE
                prev_card_sale_event.barcode       = card_sale_event.barcode
            AND prev_card_sale_event.Ticket_Number < card_sale_event.Ticket_Number_Of_Sale
    )
FROM
    #card_events card_event
        INNER JOIN card_sale_event
        ON(
                card_sale_event.Ticket_Number = card_event.Ticket_Number
            AND card_sale_event.barcode       = card_event.barcode
        )
go

--------------------------------------------------------------------------------
-- Le grand result
--------------------------------------------------------------------------------

SELECT
    barcode +
    CASE
        WHEN barcode_reuse_order > 0 THEN '(' + CONVERT(VARCHAR, barcode_reuse_order + 1) + ')'
                                     ELSE ''
    END AS barcode,
    --
    CONVERT(INT, MAX(date_created)) -
    CONVERT(INT, MIN(date_created)) AS Days_between_usage,
    --
    SUM(Credit_Or_Debit_As_Books) AS Balance
FROM #card_events
GROUP BY
    barcode +
    CASE
        WHEN barcode_reuse_order > 0 THEN '(' + CONVERT(VARCHAR, barcode_reuse_order + 1) + ')'
                                     ELSE ''
    END
go

--------------------------------------------------------------------------------
-- Clean-up
--------------------------------------------------------------------------------

DROP TABLE #card_events
go

我使用下面的脚本上传了给定的数据。

IF EXISTS(SELECT * FROM sys.tables WHERE name = 'Ticketsdetails')
DROP TABLE Ticketsdetails
go

CREATE TABLE Ticketsdetails(
    Ticket_Number   INT,
    Detail_type_ID  INT,
    Description     VARCHAR(32),
    Date_Created    DATETIME,
    TotalAmount     MONEY,
    Barcode         VARCHAR(16)
)
go

insert into Ticketsdetails values(1, 11, 'Card Sale', '20160101', 5, '123')
insert into Ticketsdetails values(1, 1, 'Book', '20160101', 5, NULL)
insert into Ticketsdetails values(1, 11, 'Card Red', '20160101', -5, '123 ')
insert into Ticketsdetails values(2, 1, 'book', '20160105', 5, NULL)
insert into Ticketsdetails values(3, 1, 'book', '20160106', 5, NULL)
insert into Ticketsdetails values(3, 11, 'Card Red', '20160106', -5, '123')
insert into Ticketsdetails values(4, 11, 'Card Sale', '20160107', 5, '124')
insert into Ticketsdetails values(5, 1, 'Book', '20160107', 5, NULL)
insert into Ticketsdetails values(5, 11, 'Card Red', '20160107', -5, '124')
insert into Ticketsdetails values(6, 11, 'Card Sale', '20160108', 5, '123')
insert into Ticketsdetails values(6, 1, 'Book', '20160108', 5, NULL)
insert into Ticketsdetails values(6, 11, 'Card Red', '20160108', -5, '123')
insert into Ticketsdetails values(7, 1, 'Book', '20160109', 5, NULL)
insert into Ticketsdetails values(7, 11, 'Card Red', '20160109', -5, '124')
go