2017-05-04 81 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行的总接受我们的例子