PIVOT SQL Server 协助

发布于 2024-10-19 14:16:30 字数 910 浏览 3 评论 0原文

给定以下表结构:

CrimeID | No_Of_Crimes  |   CrimeDate    | Violence | Robbery | ASB

1            1             22/02/2011         Y          Y      N

2            3             18/02/2011         Y          N      N

3            3             23/02/2011         N          N      Y

4            2             16/02/2011         N          N      Y

5            1             17/02/2011         N          N      Y

是否有可能使用 T-SQL 生成如下所示的结果集?

Category   |  This Week |  Last Week
Violence          1           3
Robbery           1           0
ASB               3           1

上周的数据应该小于“20/02/2011”,而本周的数据应该大于或等于“20/02/2011”,

我不是在寻找有人为我编码,尽管有一个代码代码片段会很方便:),只是一些关于这是否可能的建议,以及我应该如何使用 SQL Server 来实现它。

有关信息,我目前正在 Web 服务器上使用 LINQ 执行所有这些聚合,但这需要每次发出此请求时通过网络发送 19MB。 (该表有很多类别,并且 > 150,000 行)。我想让数据库完成所有工作,只通过网络发送少量数据

非常感谢

Given the following table structure:

CrimeID | No_Of_Crimes  |   CrimeDate    | Violence | Robbery | ASB

1            1             22/02/2011         Y          Y      N

2            3             18/02/2011         Y          N      N

3            3             23/02/2011         N          N      Y

4            2             16/02/2011         N          N      Y

5            1             17/02/2011         N          N      Y

Is there a chance of producing a result set that looks like this with T-SQL?

Category   |  This Week |  Last Week
Violence          1           3
Robbery           1           0
ASB               3           1

Where last week shuld be a data less than '20/02/2011' and this week should be greater than or equal to '20/02/2011'

I'm not looking for someone to code this out for me, though a code snippet would be handy :), just some advice on whether this is possible, and how i should go about it with SQL Server.

For info, i'm currently performing all this aggregation using LINQ on the web server, but this requires 19MB being sent over the network every time this request is made. (The table has lots of categories, and > 150,000 rows). I want to make the DB do all the work and only send a small amount of data over the network

Many thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

金兰素衣 2024-10-26 14:16:30

为了清楚起见,编辑删除了不正确的sql
编辑忘记上面的尝试下面的

select * 
from (
select wk, crime, SUM(number) number
from (
    select case when datepart(week, crimedate) = datepart(week, GETDATE()) then 'This Week'
            when datepart(week, crimedate) = datepart(week, GETDATE())-1 then 'Last Week'
            else 'OLDER' end as wk, 
            crimedate, 
            case when violence ='Y' then no_of_crimes else 0 end as violence, 
            case when robbery ='Y' then no_of_crimes else 0 end as robbery, 
            case when asb ='Y' then no_of_crimes else 0 end as asb 
    from crimetable) as src
UNPIVOT
    (number for crime in 
    (violence, robbery, asb)) as pivtab
group by wk, crime
) z
PIVOT
( sum(number)
for wk in ([This Week], [Last Week])
) as pivtab

EDIT removed incorrect sql for clarity
EDIT Forget the above try the below

select * 
from (
select wk, crime, SUM(number) number
from (
    select case when datepart(week, crimedate) = datepart(week, GETDATE()) then 'This Week'
            when datepart(week, crimedate) = datepart(week, GETDATE())-1 then 'Last Week'
            else 'OLDER' end as wk, 
            crimedate, 
            case when violence ='Y' then no_of_crimes else 0 end as violence, 
            case when robbery ='Y' then no_of_crimes else 0 end as robbery, 
            case when asb ='Y' then no_of_crimes else 0 end as asb 
    from crimetable) as src
UNPIVOT
    (number for crime in 
    (violence, robbery, asb)) as pivtab
group by wk, crime
) z
PIVOT
( sum(number)
for wk in ([This Week], [Last Week])
) as pivtab
素年丶 2024-10-26 14:16:30

虽然迟到了,但有一个具有最佳查询计划的解决方案:

示例数据

create table crimes(
    CrimeID int, No_Of_Crimes int, CrimeDate datetime,
    Violence char(1), Robbery char(1), ASB char(1));
insert crimes
select 1,1,'20110221','Y','Y','N' union all
select 2,3,'20110218','Y','N','N' union all
select 3,3,'20110223','N','N','Y' union all
select 4,2,'20110216','N','N','Y' union all
select 5,1,'20110217','N','N','Y';

制作更多数据 - 除了上述 5 行之外,总共约 10240 行,每 5 行比前 5 行早 2 周。同时创建一个索引,这将有助于在犯罪日期

insert crimes
select crimeId+number*5, no_of_Crimes, DATEADD(wk,-number*2,crimedate),
violence, robbery, asb
from crimes, master..spt_values
where type='P'

create index ix_crimedate on crimes(crimedate)

从这里开始,检查每个的输出以了解其进展情况。还要检查执行计划。

标准 Unpivot 打破类别。

select CrimeID, No_Of_Crimes, CrimeDate, Category, YesNo
from crimes
unpivot (YesNo for Category in (Violence,Robbery,ASB)) upv
where YesNo='Y'

注意:

  1. YesNo 上的过滤器实际上是在逆透视之后应用的。你可以评论出来看看。

再次逆透视,但这次仅选择上周和本周的数据。

select CrimeID, No_Of_Crimes, Category,
    Week = sign(datediff(d,CrimeDate,w.firstDayThisWeek)+0.1)
from crimes
unpivot (YesNo for Category in (Violence,Robbery,ASB)) upv
cross join (select DATEADD(wk, DateDiff(wk, 0, getdate()), 0)) w(firstDayThisWeek)
where YesNo='Y'
  and CrimeDate >= w.firstDayThisWeek -7
  and CrimeDate <  w.firstDayThisWeek +7

注意:

  1. (select DATEADD(wk, DateDiff(wk, 0, getdate()), 0)) w(firstDayThisWeek) 创建一个单列表,其中列包含此查询的关键日期,是本周的第一天(使用 DATEFIRST 设置)
  2. CrimeDate 上的过滤器实际上在取消透视之前应用于基表。检查计划
  3. Sign() 只是将数据分成 3 个桶 (-1/0/+1)。添加+0.1确保只有两个桶-1和+1。

最终查询,按本周/上周

select Category, isnull([1],0) ThisWeek, isnull([-1],0) LastWeek
from
(
    select Category, No_Of_Crimes,
        Week = sign(datediff(d,w.firstDayThisWeek,CrimeDate)+0.1)
    from crimes
    unpivot (YesNo for Category in (Violence,Robbery,ASB)) upv
    cross join (select DATEADD(wk, DateDiff(wk, 0, getdate()), -1)) w(firstDayThisWeek)
    where YesNo='Y'
      and CrimeDate >= w.firstDayThisWeek -7
      and CrimeDate <  w.firstDayThisWeek +7
) p
pivot (sum(No_Of_Crimes) for Week in ([-1],[1])) pv
order by Category Desc

输出旋转

Category  ThisWeek    LastWeek
--------- ----------- -----------
Violence  1           3
Robbery   1           0
ASB       3           3

Late to the party, but a solution with an optimal query plan:

Sample data

create table crimes(
    CrimeID int, No_Of_Crimes int, CrimeDate datetime,
    Violence char(1), Robbery char(1), ASB char(1));
insert crimes
select 1,1,'20110221','Y','Y','N' union all
select 2,3,'20110218','Y','N','N' union all
select 3,3,'20110223','N','N','Y' union all
select 4,2,'20110216','N','N','Y' union all
select 5,1,'20110217','N','N','Y';

Make more data - about 10240 rows in total in addition to the 5 above, each 5 being 2 weeks prior to the previous 5. Also create an index that will help on crimedate.

insert crimes
select crimeId+number*5, no_of_Crimes, DATEADD(wk,-number*2,crimedate),
violence, robbery, asb
from crimes, master..spt_values
where type='P'

create index ix_crimedate on crimes(crimedate)

From here on, check output of each to see where this is going. Check also the execution plan.

Standard Unpivot to break the categories.

select CrimeID, No_Of_Crimes, CrimeDate, Category, YesNo
from crimes
unpivot (YesNo for Category in (Violence,Robbery,ASB)) upv
where YesNo='Y'

Notes:

  1. The filter on YesNo is actually applied AFTER unpivoting. You can comment it out to see.

Unpivot again, but this time select data only for last week and this week.

select CrimeID, No_Of_Crimes, Category,
    Week = sign(datediff(d,CrimeDate,w.firstDayThisWeek)+0.1)
from crimes
unpivot (YesNo for Category in (Violence,Robbery,ASB)) upv
cross join (select DATEADD(wk, DateDiff(wk, 0, getdate()), 0)) w(firstDayThisWeek)
where YesNo='Y'
  and CrimeDate >= w.firstDayThisWeek -7
  and CrimeDate <  w.firstDayThisWeek +7

Notes:

  1. (select DATEADD(wk, DateDiff(wk, 0, getdate()), 0)) w(firstDayThisWeek) makes a single-column table where the column contains the pivotal date for this query, being the first day of the current week (using DATEFIRST setting)
  2. The filter on CrimeDate is actually applied on the BASE TABLE prior to unpivoting. Check plan
  3. Sign() just breaks the data into 3 buckets (-1/0/+1). Adding +0.1 ensures that there are only two buckets -1 and +1.

The final query, pivoting by this/last week

select Category, isnull([1],0) ThisWeek, isnull([-1],0) LastWeek
from
(
    select Category, No_Of_Crimes,
        Week = sign(datediff(d,w.firstDayThisWeek,CrimeDate)+0.1)
    from crimes
    unpivot (YesNo for Category in (Violence,Robbery,ASB)) upv
    cross join (select DATEADD(wk, DateDiff(wk, 0, getdate()), -1)) w(firstDayThisWeek)
    where YesNo='Y'
      and CrimeDate >= w.firstDayThisWeek -7
      and CrimeDate <  w.firstDayThisWeek +7
) p
pivot (sum(No_Of_Crimes) for Week in ([-1],[1])) pv
order by Category Desc

Output

Category  ThisWeek    LastWeek
--------- ----------- -----------
Violence  1           3
Robbery   1           0
ASB       3           3
吻泪 2024-10-26 14:16:30

我会尝试这个:

declare @FirstDayOfThisWeek date = '20110220';
select cat.category, 
       ThisWeek = sum(case when cat.CrimeDate >= @FirstDayOfThisWeek
                     then crt.No_of_crimes else 0 end), 
       LastWeek = sum(case when cat.CrimeDate >= @FirstDayOfThisWeek
                     then 0 else crt.No_of_crimes end)
from crimetable crt
cross apply (values 
    ('Violence', crt.Violence),
    ('Robbery', crt.Robbery),
    ('ASB', crt.ASB))
    cat (category, incategory)
where cat.incategory = 'Y'
  and crt.CrimeDate >= @FirstDayOfThisWeek-7
group by cat.category;

I would try this:

declare @FirstDayOfThisWeek date = '20110220';
select cat.category, 
       ThisWeek = sum(case when cat.CrimeDate >= @FirstDayOfThisWeek
                     then crt.No_of_crimes else 0 end), 
       LastWeek = sum(case when cat.CrimeDate >= @FirstDayOfThisWeek
                     then 0 else crt.No_of_crimes end)
from crimetable crt
cross apply (values 
    ('Violence', crt.Violence),
    ('Robbery', crt.Robbery),
    ('ASB', crt.ASB))
    cat (category, incategory)
where cat.incategory = 'Y'
  and crt.CrimeDate >= @FirstDayOfThisWeek-7
group by cat.category;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文