2016-03-08 26 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