2017-05-04 80 views
2

我有桌子,我把它叫做手動值,稍後在我的代碼中使用。此表看起來像這樣:按天分配行均勻

subId | MonthNo | PackagesNumber | Country | EntryMethod | PaidAmount | Version 
1  | 201701 | 223   | NO  | BCD   | 44803  | 2 
2  | 201701 | 61    | NO  | GHI   | 11934  | 2 
3  | 201701 | 929   | NO  | ABC   | 88714  | 2 
4  | 201701 | 470   | NO  | DEF   | 98404  | 2 
5  | 201702 | 223   | NO  | BCD   | 28225  | 2 

我所要做的就是將這些值分成單行,在單個包的級別。例如,2017年1月在EntryNethod BCD的Country NO中有223個包,所以我需要223個單獨的行。 PaidAmount也應該被PackagesNumber的數量除。

問題是我必須將日期關聯到每條記錄。記錄應該整個月均勻分發。我有日期維度,我可以通過從MontNo分別拉月和年來與我的表相交。 例如,2017年1月,EntryMethod BCD我擁有軟件包,所以它每天約7個軟件包。

這就是我想要的東西:

subId | Date  | Country | Packages | EntryMethod | PaidAmount  | Version 
1  | 01.01.2017 | NO  | 1  | BCD   | 200.910313901345 | 2 
2  | 01.01.2017 | NO  | 1  | BCD   | 200.910313901345 | 2 
3  | 01.01.2017 | NO  | 1  | BCD   | 200.910313901345 | 2 
4  | 01.01.2017 | NO  | 1  | BCD   | 200.910313901345 | 2 
5  | 01.01.2017 | NO  | 1  | BCD   | 200.910313901345 | 2 
6  | 01.01.2017 | NO  | 1  | BCD   | 200.910313901345 | 2 
7  | 01.01.2017 | NO  | 1  | BCD   | 200.910313901345 | 2 
8  | 02.01.2017 | NO  | 1  | BCD   | 200.910313901345 | 2 

獎勵:我寫的代碼,這將包成單的記錄,它把每個月的第一天日期。

SELECT 
     Date = 
(
    SELECT TOP 1 
      date 
    FROM dim_Date dim 
    WHERE dim.Month = a.Month 
      AND dim.Year = a.Year 
) 
    , Country 
    , EntryMethod 
    , Deliveries = 1 
    , PaidAmount = NULLIF(PaidAmount, 0)/PackagesNumber 
    , SubscriptionId = 90000000 + ROW_NUMBER() OVER(ORDER BY n.number) 
    , Version 
FROM 
(
    SELECT 
      [Year] = LEFT(MonthNo, 4) 
     , [Month] = RIGHT(MonthNo, 2) 
     , Country 
     , EntryMethod 
     , PackagesNumber 
     , PaidAmount 
     , Version 
    FROM tgm.rep_PredictionsReport_ManualValues tgm 

    /*WHERE MonthNo = 201701*/ 
) a 
JOIN master..spt_values n 
ON n.type = 'P' 
    AND n.number < CAST(PackagesNumber AS INT); 

編輯:我取得了一些進展。我使用了NTILE函數,將行分成組。 唯一改變的是日期從頂級選擇。它看起來像現在:

Date = concat([Year], '-', [Month], '-', case when ntile(31) over(order by n.number) < 10 then '0' + cast(ntile(31) over(order by n.number) as varchar(2)) else cast(ntile(31) over(order by n.number) as varchar(2)) end) 

說明:我創建日期使用年和領域提交,NTILE過在一個月的天數(現在它是靜態的數字,但後來改變) 。結果並不像我預期的那樣好,它創造的團體應該是它們的兩倍(14日而不是7日)。

+0

我覺得它比我雖然簡單得多:)有人建議我使用NTILE窗口功能。它正是我想要的。當我完成後,會發布答案。 – Dodzik

+0

現在我再讀一遍,我想我的確誤解了這個問題。你想要你的結果作爲一個查詢,我想你想他們作爲INSERTs在表 –

+0

@ThomasG這是無關緊要,如果它是查詢或插入的結果。只是算法很重要 – Dodzik

回答

2

您可以使用模運算符完成此操作,該運算符允許您將項目分成一組數目的類別。

這是一個完整的測試:http://rextester.com/TOROA96856

下面是相關查詢:

--recursive query to expand each row. 
with expand_rows (subid,monthno,month,packagesnumber,paidamount) as (
    select subid,monthno,month,packagesnumber,(paidamount+0.0000)/packagesnumber 
     from initial_table 
    union all 
    select subid,monthno,month,packagesnumber-1,paidamount 
     from expand_rows where packagesnumber >1 
) 
select expand_rows.*,(packagesnumber % numdays)+1 day, paidamount from expand_rows 
    join dayspermonth d on 
     d.month = expand_rows.month 
    order by subid, day 
    option (maxrecursion 0) 

(packagesnumber % numdays)+1是指定項目,每天的取模操作。

請注意,我預先計算了每個月的天數以便在查詢中使用的表格。爲了答案的目的,我也簡化了這個問題(添加了一個純月份列,因爲我不想複製日期維度)。

如果您關心的是當事物不能均勻分配時(例如,如果您在一月份有32個項目,哪一天有額外的項目?),您可能需要調整模數查詢。在這個例子中,本月的第二天趨向於獲得最多(因爲將月份的最後一天結束爲0的事實加上1)。如果您希望額外的日子在月初,您可以使用case語句將0轉換爲月份中的天數,而不是。

0

要分發223號均勻塗抹於一月日子裏,我們這樣做: 共有31天,一月 的用於三分之二百二十三其餘6 三十一分之二百二十三是7(整數除法)

因此多數民衆贊成7記錄公關日,另加1月1日至6日的額外紀錄。

我已經使用了符合表以日期和多一些,但行的發行pr一天可以這樣確定:

with 
tally as 
(
select row_number() over (order by n)-1 n from 
(values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) n(n) 
cross join (values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) m(m) 
cross join (values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) l(m) 
cross join (values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) k(m) 
) 
,t1 as 
(
select 
* 
from 
(values 

(1  , 201701 , 223   , 'NO'  , 'BCD'   , 44803  , 2) 
,(2  , 201701 , 61    , 'NO'  , 'GHI'   , 11934  , 2) 
,(3  , 201701 , 929   , 'NO'  , 'ABC'   , 88714  , 2) 
,(4  , 201701 , 470   , 'NO'  , 'DEF'   , 98404  , 2) 
,(5  , 201702 , 223   , 'NO'  , 'BCD'   , 28225  , 2) 
) t(subId , MonthNo , PackagesNumber , Country , EntryMethod , PaidAmount , Version) 
) 
,dates as 
(
select dateadd(day,n,'20170101') as dt 
    ,convert(varchar(10),dateadd(day,n,'20170101'),112)/100 mnthkey 
    ,day(dateadd(day,-1,dateadd(month,1,cast(((convert(varchar(10),dateadd(day,n,'20170101'),112)/100)*100 +1) as varchar(10))))) DaysInMonth 
from 
tally 
) 
select 
    subId 
    ,MonthNo 
    ,dt 
    ,PackagesNumber 
    ,case when day(dt)<=PackagesNumber%DaysInMonth then 1 else 0 end remainder 
    ,PackagesNumber/DaysInMonth evenlyspread 
    ,Country 
    ,EntryMethod 
    ,PaidAmount 
    ,Version 
from t1 a 
inner join dates b 
on a.MonthNo=b.mnthkey 

我加入與數據表中的月份,而對於在本月的每一天,我分配均勻分配的天數,在我們的示例中爲7,在我們的示例中爲第一天,6,我加上1作爲餘數

現在我們從您的基表中獲得信息,相關的幾個月,現在我們只需要在多個行的每一天,這裏我們再次使用理貨tabe:

with 
tally as 
(
select row_number() over (order by n)-1 n from 
(values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) n(n) 
cross join (values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) m(m) 
cross join (values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) l(m) 
cross join (values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) k(m) 
) 
,t1 as 
(
select 
* 
from 
(values 

(1  , 201701 , 223   , 'NO'  , 'BCD'   , 44803  , 2) 
,(2  , 201701 , 61    , 'NO'  , 'GHI'   , 11934  , 2) 
,(3  , 201701 , 929   , 'NO'  , 'ABC'   , 88714  , 2) 
,(4  , 201701 , 470   , 'NO'  , 'DEF'   , 98404  , 2) 
,(5  , 201702 , 223   , 'NO'  , 'BCD'   , 28225  , 2) 
) t(subId , MonthNo , PackagesNumber , Country , EntryMethod , PaidAmount , Version) 
) 
,dates as 
(
select dateadd(day,n,'20170101') as dt 
    ,convert(varchar(10),dateadd(day,n,'20170101'),112)/100 mnthkey 
    ,day(dateadd(day,-1,dateadd(month,1,cast(((convert(varchar(10),dateadd(day,n,'20170101'),112)/100)*100 +1) as varchar(10))))) DaysInMonth 
from 
tally 
) 
,forshow as 
(
select 
    subId 
    ,MonthNo 
    ,dt 
    ,PackagesNumber 
    ,case when day(dt)<=PackagesNumber%DaysInMonth then 1 else 0 end remainder 
    ,PackagesNumber/DaysInMonth evenlyspread 
    ,Country 
    ,EntryMethod 
    ,(PaidAmount+0.0000)/(PackagesNumber*1.0000) PaidAmount 
    ,Version 
    ,PaidAmount TotalPaidAmount 
from t1 a 
inner join dates b 
on a.MonthNo=b.mnthkey 
) 
select 
    subId 
    ,dt [Date] 
    ,Country 
    ,1 Packages 
    ,EntryMethod 
    ,PaidAmount 
    ,Version 
    -- the following rows are just for control 
    ,remainder+evenlyspread toalday 
    ,count(*) over (partition by subId,MonthNo,dt) calctotalday 
    ,PackagesNumber 
    ,count(*) over (partition by subId) calcPackagesNumber 
    ,sum(PaidAmount)over (partition by subId) calcPaidAmount 
    ,TotalPaidAmount 
from forshow 
inner join tally on n<(remainder+evenlyspread) 

order by subId,MonthNo,dt 

我加入的天數(evenspread +餘數),並得到一行pr包。

我已經添加了一些列的檢查,以確保我拿到8行的第6天,而223行的總接受我們的例子