在SQL Server中对日期范围进行排序(排名)季度

时间:2017-07-31 12:10:33

标签: sql-server tsql

我很少在T-SQL报告查询中使用while循环。事实上,这可能是我们整个报告系统中唯一的看法。因此,我决定检查是否有人有更好的方法来解决以下问题。

您有一份报告,您必须查询两年的交易。其中一列是日历季度排名。我将在一分钟内解释这是什么。

每个日历日期都有一个财务季度。我相信这是普遍的,但从美国的角度来看,1月1日到3月30日是一年的第1季度,4月1日到6月30日是一年的第2季度,依此类推。

我想要做的是从给定的日期范围中取出所有季度并按数字排名。这意味着如果我的报告从2012年4月1日开始并持续到2014年3月30日(八个季度),那么2012年4月1日到2012年6月30日之间的日期将被排列为第1季度(对于我的报告)。

2012年7月1日的日期将被列为第2季度。

2014年3月30日的交易日期将落入我的报告的第8季度。

为了解决这个问题,我使用了一个while循环来旋转报告范围中的日期,并在季度增加1时指定数字季度排名(日期从2012年6月30日到2012年7月1日,例如)。

这是代码......

    declare @tv_transactions table(recnum int identity(1,1) primary key, item varchar(50), amt money, txndate datetime)

insert into @tv_transactions(item, amt, txndate)values('iPod Shuffle', 130, '2012-04-03')
insert into @tv_transactions(item, amt, txndate)values('xBox One S', 299, '2012-05-12')
insert into @tv_transactions(item, amt, txndate)values('iMac Pro', 4999, '2012-07-23')
insert into @tv_transactions(item, amt, txndate)values('Flash Stick', 4.50, '2012-08-01')
insert into @tv_transactions(item, amt, txndate)values('Pencils', 3.67, '2012-10-23')
insert into @tv_transactions(item, amt, txndate)values('Markers', 4.99, '2012-11-20')
insert into @tv_transactions(item, amt, txndate)values('Windex', 1.99, '2013-01-23')
insert into @tv_transactions(item, amt, txndate)values('echo dot', 49, '2013-02-14')
insert into @tv_transactions(item, amt, txndate)values('Eggs', 3.99, '2013-04-12')
insert into @tv_transactions(item, amt, txndate)values('Strawberries', 2.99, '2013-06-23')
insert into @tv_transactions(item, amt, txndate)values('Portable Generator', 880, '2013-08-12')
insert into @tv_transactions(item, amt, txndate)values('Mattress Pad', 103.99, '2013-09-03')
insert into @tv_transactions(item, amt, txndate)values('Power Drill', 49.99, '2013-10-11')
insert into @tv_transactions(item, amt, txndate)values('GT Road GT', 14.77, '2013-11-20')
insert into @tv_transactions(item, amt, txndate)values('QFit Bluetoot Speaker', 25.99, '2014-01-31')
insert into @tv_transactions(item, amt, txndate)values('Toilet Night Light', 8.99, '2014-02-23')
insert into @tv_transactions(item, amt, txndate)values('BS-MALL Makeup Brushes', 6.29, '2014-03-14')



declare    @startdate              datetime
         , @enddate                datetime
         , @datecount              datetime
         , @year                   int
         , @quarter                int
         , @quartercount           int  

declare @tv_quarters table(quarternum int primary key, year int, quarter int)

/****************************************************************************
 * Inatialize Variables                                                     *
 ****************************************************************************/
select     @startdate               =  '2012-04-01'
        ,  @enddate                 =  '2014-03-30'
        ,  @datecount               =  null

select     @datecount               =  @startdate

select     @year                    =  year(@datecount)
        ,  @quarter                 =  datepart(quarter, @datecount)          
        ,  @quartercount            =  1


insert into @tv_quarters(quarternum, year, quarter) values(@quartercount, @year, @quarter)
select @datecount = dateadd(day, 1, @datecount)

while(@datecount <= @enddate)
begin
   select    @year     =  null
          ,  @quarter  =  null

   select    @year     = year
          ,  @quarter  = quarter
   from @tv_quarters
   where quarternum = @quartercount

   if(year(@datecount) <> @year or datepart(quarter, @datecount) <> @quarter)
   Begin
      select @quartercount = @quartercount + 1
      insert into @tv_quarters(quarternum, year, quarter) 
      select @quartercount, year(@datecount), datepart(quarter, @datecount)
    End 
    select @datecount = dateadd(day, 1, @datecount)   
end

select t.item, t.amt, t.txndate, q.quarternum
from @tv_transactions t
inner join @tv_quarters q on q.year = year(t.txndate) and q.quarter = datepart(quarter, t.txndate)
order by q.quarternum asc

有没有更好的方法来解决这个问题?有没有办法避免使用while循环?

我的公司目前正在使用SQL Server 2005(嘿嘿嘿嘿嘲笑我),但我们正在迁移到SQL 2016,所以我对所有建议持开放态度。

谢谢

2 个答案:

答案 0 :(得分:3)

更新回答:

如果您希望const options = { tooltips: { enabled: false, custom: (tooltip) => { // Retrieving valuable props from tooltip (caretX, caretY) // and creating custom tooltip that is positioned // on top of a bar } } // other options } const chart = new Chart(ctx, { type: 'bar', data, options }) 只代表该季度的序数与quarternum相比,那么您可以使用@startdate

datediff()

rextester演示:http://rextester.com/GBO47881

返回:(删除了两行以显示间隙)

declare    @startdate              datetime
         , @enddate                datetime
select     @startdate               =  '2012-04-01'
        ,  @enddate                 =  '2014-04-01';
select 
    t.item
  , t.amt
  , t.txndate
  , quarternum = datediff(quarter, @startdate, t.txndate)+1
from @tv_transactions t
order by quarternum asc

您可以使用dense_rank() introduced in SQL Server 2005,并且因为您的季度与日历年一致,您可以使用+------------------------+-----------+------------+------------+ | item | amt | txndate | quarternum | +------------------------+-----------+------------+------------+ | iMac Pro | 4999,0000 | 2012-07-23 | 2 | | Flash Stick | 4,5000 | 2012-08-01 | 2 | | Pencils | 3,6700 | 2012-10-23 | 3 | | Markers | 4,9900 | 2012-11-20 | 3 | | Windex | 1,9900 | 2013-01-23 | 4 | | echo dot | 49,0000 | 2013-02-14 | 4 | | Eggs | 3,9900 | 2013-04-12 | 5 | | Strawberries | 2,9900 | 2013-06-23 | 5 | | Portable Generator | 880,0000 | 2013-08-12 | 6 | | Mattress Pad | 103,9900 | 2013-09-03 | 6 | | Power Drill | 49,9900 | 2013-10-11 | 7 | | GT Road GT | 14,7700 | 2013-11-20 | 7 | | QFit Bluetoot Speaker | 25,9900 | 2014-01-31 | 8 | | Toilet Night Light | 8,9900 | 2014-02-23 | 8 | | BS-MALL Makeup Brushes | 6,2900 | 2014-03-14 | 8 | +------------------------+-----------+------------+------------+ year()而无需对其进行任何修改排名顺序:

datepart(quarter,...)

rextester演示:http://rextester.com/YMPVMQ59269

返回:

select 
    t.item
  , t.amt
  , t.txndate
  , quarternum = dense_rank() over (order by year(t.txndate), datepart(quarter,t.txndate))
from @tv_transactions t
order by quarternum asc

答案 1 :(得分:1)

您可以使用带有递归CTE(SQL Server 2005 +)的可自定义解决方案:

DECLARE @startdate datetime = '2012-04-01',
        @enddate   datetime = '2014-04-01';

WITH dates AS (
     SELECT 1 AS quarternum, @startdate AS startDate, DATEADD(mm, 3, @startdate) AS endDate
     UNION ALL
     SELECT t.quarternum + 1, t.endDate, DATEADD(mm, 3, t.endDate) FROM dates t WHERE DATEADD(mm, 3, t.endDate) <= @enddate
)
SELECT tt.item, tt.amt, tt.txndate, d.quarternum
    FROM @tv_transactions tt
    INNER JOIN dates d ON d.startDate <= tt.txndate AND d.endDate > tt.txndate;

输出:

item                     amt       txndate     quarternum
---------------------------------- ----------- -----------
iPod Shuffle             130,00    2012-04-03  1
xBox One S               299,00    2012-05-12  1
iMac Pro                 4999,00   2012-07-23  2
Flash Stick              4,50      2012-08-01  2
Pencils                  3,67      2012-10-23  3
Markers                  4,99      2012-11-20  3
Windex                   1,99      2013-01-23  4
echo dot                 49,00     2013-02-14  4
Eggs                     3,99      2013-04-12  5
Strawberries             2,99      2013-06-23  5
Portable Generator       880,00    2013-08-12  6
Mattress Pad             103,99    2013-09-03  6
Power Drill              49,99     2013-10-11  7
GT Road GT               14,77     2013-11-20  7
QFit Bluetoot Speaker    25,99     2014-01-31  8
Toilet Night Light       8,99      2014-02-23  8
BS-MALL Makeup Brushes   6,29      2014-03-14  8

请注意@enddate已更改。