2016-03-08 29 views
0

我有一票信息表,用于存储交易信息之间的天数。这里是一个表格数据的例子:如何找到结果

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'表示卡销售或赎回。基于以上数据,这是我在寻找的输出:

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 
+0

向我解释更多关于“余额仍然存在”的业务逻辑。把它作为第三列而不是另一行可能更容易,但我想这取决于你需要什么。 –

+0

也是票号。票号如何与其余数据相关联? –

+0

我其实只是想编辑我的问题,使其成为第三列,因为我试图通过这个工作。 – Shmewnix

回答

5

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

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

这太棒了!看起来像它给了我我需要的东西 - 谢谢! – Shmewnix

0

你可以做到这一点自联接或子查询。

下面是一些伪代码

SELECT t1.Barcode, SUM(t1.Date - t2.Date) 
FROM TheTable t1 
JOIN TheTable t2 ON t1.Barcode=t2.Barcode 
AND t1.Code = 'Redemption' 
AND t2.Code = 'Sale' 
GROUP BY Barcode 

然后得到那个最后一排,你必须与UNION生成你想要的行另一个查询。您可以使用CASE表达式根据余额是否大于0来生成字符串。并且可以使用子查询生成条形码旁边括号中的数字。

+0

这是卡片(本质上是条形码列)重新使用吗? –

+0

以什么方式考虑它?在你的问题中,我没有看到有关如何考虑这一点的问题。 –

+0

在最后一段我说:“正如你将会看到的那样 - 条形码存储在细节表中,但是,我们重复使用这些卡,所以我们不希望这样会污染第一张卡的数据。” – Shmewnix

0

有点复杂的代码波纹管为您提供了解决方案。

我必须说 - 在现有场景中,重复使用条形码的人会强制您的解决方案总是在整个销售历史中向后看,以便在给定的环境中设计“重用事件的顺序”。

你最好不要重复使用条码或非规范化的“重用秩序”作为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