【问题标题】:How to count incrementally in SQL Server如何在 SQL Server 中进行增量计数
【发布时间】:2011-06-08 18:02:54
【问题描述】:

我被分配了一个问题,我不确定如何在 SQL Server(版本 5/8)中解决。这是简单的查询及其输出:

Select location, date_time, Item_sold
From Product

Location        Date_time                   Item_sold
VA            12/10/2010 1:30:00 PM           Candy
VA            12/10/2010 3:30:00 PM           Chips
VA            12/13/2010 12:50:00 AM          Wine
DC            12/13/2010 8:00:00 AM           Gum
DC            12/13/2010 12:30:00 PM          Bags
DC            12/13/2010 1:16:00 PM           Cheese
DC            12/13/2010 12:00:00 AM          Hotdog
NJ            12/14/2010 12:00:00 AM          Coffee
NJ            12/14/2010 1:15:00 PM           Beers
NJ            12/14/2010 12:00:00 AM          Coffee
NJ            12/14/2010 1:45:00 PM           Water

这是我想要的输出,我猜一个 while/for 循环或一个 pivot 函数可以完成这项工作,但我的经验还没有。基本上,我需要从 Item_sold 列增量计算已售出的商品数量(基线日期从 12/8 到 12/9、12/8 到 12/10、12/8 到 12/11、12/8 到12 月 12 日...)

Location    12/10 to 12/11  12/10 to 12/12  12/10 to 12/13  12/10 to 12/14
VA               2             2                  3              3
DC               0             0                  3              3 
NJ                 0             0                  0              4

我该如何解决这个问题?

【问题讨论】:

  • 我认为你的 12/8 - 12/11 和 12/8 - 12/12 对于 VA 应该是 2,不是吗?
  • @joe - 您能否确认它应该为 NJ 设置一行,并且 VA 不应该是 0/0/2/2/2/3?为什么 2 和 3 之间有 0?
  • 而且查询应该是动态的?
  • @joe - 为什么正好有 6 列,你能提供一个日期范围吗?
  • @cyberkiwi,是的,它应该是 NY 的一行,但我只展示了 VA 和 DC 作为示例。日期范围是 12/8/10 到 1/5/11。

标签: sql sql-server tsql pivot


【解决方案1】:

示例表和数据

create table Product(Location char(2), Date_time datetime, Item_sold varchar(20))
insert Product select 'VA', '20101210 1:30:00 PM' ,'Candy'
insert Product select 'VA', '20101210 3:30:00 PM' ,'Chips'
insert Product select 'VA', '20101213 12:50:00 AM' ,'Wine'
insert Product select 'DC', '20101213 8:00:00 AM' ,'Gum'
insert Product select 'DC', '20101213 12:30:00 PM' ,'Bags'
insert Product select 'DC', '20101213 1:16:00 PM' ,'Cheese'
insert Product select 'DC', '20101213 12:00:00 AM' ,'Hotdog'
insert Product select 'NJ', '20101215 12:00:00 AM' ,'Coffee'
insert Product select 'NJ', '20101215 1:15:00 PM' ,'Beers'
insert Product select 'NJ', '20101215 3:45:00 AM' ,'Cream'

生成所需结果的 T-SQL

declare @start datetime
declare @end datetime
select @start = '20101208', @end = '20110105'

declare @sql nvarchar(max);
-- generate the column names
select @sql = coalesce(@sql + ',', '') + QuoteName(Convert(char(5),@start,101)+' - '+Convert(char(5),DT,101))
from (
    select @start + number DT
    from master..spt_values
    where type='P' and number between 0 and DATEDIFF(D,@start,@end)) T;

-- replace the column names into the generic PIVOT form
set @sql = REPLACE('
;with COUNTS AS (
    select p.location, Convert(char(5),@start,101)+'' - ''+Convert(char(5),@start + v.number,101) DT, X.C
    from 
    (
        Select distinct location From Product
        where Date_time >= @start and Date_time < @end+1 -- * the date after, to handle the times
    ) p
    inner join master..spt_values v on v.type=''P'' and v.number between 0 and DATEDIFF(D,@start,@end)
    cross apply
    (
        select COUNT(*) C from product p2
        where p2.Location=p.Location
        and p2.date_time >= @start and p2.date_time < @start + v.number +1
    ) X
)
select location, :columns:
from COUNTS p
pivot (max(C) for DT in (:columns:)) pv',
':columns:', @sql)

-- execute for the results
exec sp_executesql @sql, N'@start datetime,@end datetime', @start, @end

【讨论】:

  • 谢谢您,现在测试您的脚本。我还更新了输出以使其更易于理解。给你一个问题,假设我创建了一个包含大量数据的临时表(日期范围从 10/1/09 到 10/1/10),你的脚本可以工作吗?
  • 我刚刚执行了脚本,出现了几个错误。以下是脚本错误:关键字“inner”附近的语法不正确。 “X”附近的语法不正确。
  • @joe - 我只是将两个块按原样复制并粘贴到 ssms 中,然后在 tempdb 中运行它,它给出的结果没有错误。那是你做的吗?您能否确认 SQL Server 版本 >= 2005?
  • 它是 MS SQL Server 2000/5/8 版本。感谢您的其他建议。
【解决方案2】:

以上来自cyberkiwi,太复杂了。你需要做一些叫做“桌子旋转”的事情。假设你有一个这样的产品表:

create table product
(
  location  char(2)     not null ,
  date_time datetime    not null ,
  item_sold varchar(32) not null ,

  primary key clustered ( location , date_time ) ,
)

你想要的sql是下面的SELECT语句:

select Location       = loc.location  ,
       "12/8"         =  t8.sales_cnt ,
       "12/8 - 12/09" =  t9.sales_cnt ,
       "12/8 - 12/10" = t10.sales_cnt ,
       "12/8 - 12/11" = t11.sales_cnt ,
       "12/8 - 12/12" = t12.sales_cnt ,
       "12/8 - 12/13" = t13.sales_cnt
from      ( select distinct location from #product ) loc
left join ( select location , sales_cnt = count(*) from #product where date_time between '2011-12-08 00:00:00.000' and '2011-12-08 23:59:59.997' group by location ) t8  on t8.location  = loc.location
left join ( select location , sales_cnt = count(*) from #product where date_time between '2011-12-08 00:00:00.000' and '2011-12-09 23:59:59.997' group by location ) t9  on t9.location  = loc.location
left join ( select location , sales_cnt = count(*) from #product where date_time between '2011-12-08 00:00:00.000' and '2011-12-10 23:59:59.997' group by location ) t10 on t10.location = loc.location
left join ( select location , sales_cnt = count(*) from #product where date_time between '2011-12-08 00:00:00.000' and '2011-12-11 23:59:59.997' group by location ) t11 on t11.location = loc.location
left join ( select location , sales_cnt = count(*) from #product where date_time between '2011-12-08 00:00:00.000' and '2011-12-12 23:59:59.997' group by location ) t12 on t12.location = loc.location
left join ( select location , sales_cnt = count(*) from #product where date_time between '2011-12-08 00:00:00.000' and '2011-12-13 23:59:59.997' group by location ) t13 on t13.location = loc.location
order by 1

【讨论】:

  • 不用太复杂,您的代码将如何针对可变的日期范围进行扩展?我一直不喜欢 BETWEEN + 'xxx 23:59:59.997' 方法,特别是因为您现在可以在 SQL Server 2008 中获得更接近午夜的日期。小于次日似乎效果最好并且准确地代表了这种情况。
  • 谢谢。这适用于上面的示例数据,但是当您拥有更大的数据集时,您会怎么做?
猜你喜欢
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多