【问题标题】:Distribute rows evenly by days按天均匀分布行
【发布时间】:2017-05-04 09:03:50
【问题描述】:

我有一个表,我把它称为手动值,稍后在我的代码中使用。这张表是这样的:

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 月在 Country NO 中使用 EntryMethod BCD 的包裹有 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)

说明:我正在使用 YearMonth 字段创建日期归档,并且 NTILE 超过月份的天数(现在它是静态数字,但稍后会更改) .结果并不像我预期的那样好,它创建的组是应有的两倍(每个日期 14 行而不是 7 行)。

【问题讨论】:

  • 我认为它比我简单得多:) 有人建议我使用 NTILE 窗口函数。它正是我想要的。完成后将发布答案。
  • 现在我再看一遍,我想我确实误解了这个问题。您希望将结果作为查询,我以为您希望将它们作为表中的 INSERT
  • @ThomasG 它是查询还是插入的结果都没有关系。只是算法很重要

标签: sql tsql business-intelligence sql-server-2016


【解决方案1】:

您可以使用模运算符来完成此操作,它允许您将项目划分为一定数量的类别。

这是一个完整的测试: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 个项目,哪一天有额外的项目?)。在这个例子中,一个月的第二天往往得到最多(因为加 1 以说明一个月的最后一天最终为 0 的事实)。如果您希望额外的天数落在月初,您可以使用将 0 转换为当月天数的 case 语句。

【讨论】:

    【解决方案2】:

    为了在 1 月的几天内平均分配 223 个数字,我们这样做: 一月有31天 223/3 的余数是 6 223/31 是 7(整数除法)

    这就是每天 7 条记录,加上 1 月 1 日至 6 日的 1 条记录。

    我使用了一个计数表来制作日期等等,但是可以像这样确定行的分布:

    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 作为余数

    现在我们从你的基表中得到信息,乘以相关月份中的每一天,现在我们只需要在一天中创建多行,这里我们再次使用计数表:

    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
    

    我加入天数(evenlyspread+remainder),得到一排pr包。

    我添加了一些检查列,以确保在前 6 天得到 8 行,在我们的示例中总共得到 223 行

    【讨论】:

      猜你喜欢
      • 2011-04-04
      • 1970-01-01
      • 2023-02-26
      • 2011-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-20
      • 2021-12-16
      相关资源
      最近更新 更多