是否有更有效的方法在SQL Server 2008中按日期分组

时间:2012-12-12 21:25:22

标签: sql sql-server-2008

我必须创建一个报告,其中AccountSegment为行,2周的日期范围为列标题。列值将是表中具有相关段/日期范围的记录数的计数。

所以期望的输出看起来像这样:

AcctSeg  4/9/12-4/20/12   4/23/12-5/4/12   5/7/12-5/18/12
Segment1       100             200              300
Segment2       110             220              330
Segment3       120             230              340

以下查询执行我想要的操作,但看起来效率低下且难看。我想知道是否有更好的方法来完成同样的事情:

SELECT
    AccountSegment = S.Segment_Name,
    '4/9/2012 - 4/20/2012' = SUM(CASE WHEN date_start BETWEEN '2012-04-09' AND '2012-04-20' THEN 1 END),
    '4/23/2012 - 5/4/2012' = SUM(CASE WHEN date_start BETWEEN '2012-04-23' AND '2012-05-04' THEN 1 END),
    '5/7/2012 - 5/18/2012' = SUM(CASE WHEN date_start BETWEEN '2012-05-07' AND '2012-05-18' THEN 1 END),
    '5/21/2012 - 6/1/2012' = SUM(CASE WHEN date_start BETWEEN '2012-05-21' AND '2012-06-01' THEN 1 END),
    '6/4/2012 - 6/15/2012' = SUM(CASE WHEN date_start BETWEEN '2012-06-04' AND '2012-06-15' THEN 1 END),
    '6/18/2012 - 6/29/2012' = SUM(CASE WHEN date_start BETWEEN '2012-06-18' AND '2012-06-29' THEN 1 END),
    '7/2/2012 - 7/13/2012' = SUM(CASE WHEN date_start BETWEEN '2012-07-02' AND '2012-07-13' THEN 1 END),
    '7/16/2012 - 7/27/2012' = SUM(CASE WHEN date_start BETWEEN '2012-07-16' AND '2012-07-27' THEN 1 END),
    '7/30/2012 - 8/10/2012' = SUM(CASE WHEN date_start BETWEEN '2012-07-30' AND '2012-08-10' THEN 1 END)
FROM
    dbo.calls C
    JOIN dbo.accounts a ON C.parent_id = a.id
    JOIN dbo.accounts_cstm a2 ON a2.id_c = A.id
    JOIN dbo.Segmentation S ON a2.[2012_segmentation_c] = S.Segment_Num
WHERE
    c.deleted = 0 
GROUP BY
    S.Segment_Name
ORDER BY
    MIN(S.Sort_Order)

2 个答案:

答案 0 :(得分:2)

看起来不错,但我建议改进一项性能:

where c.deleted = 0 and
      date_start between  '2012-04-09' AND '2012-08-10'

这会将聚合仅限制为您需要的行。 。 。除非你想要用空数据列出所有内容。

我倾向于将else 0添加到case语句中,因此显示0而不是NULL。

答案 1 :(得分:1)

@PaulStock,很高兴这样做。

这种技术发挥了RDMS的优势,即数据检索和集合操作 - 将其改为其他编程语言,如C#更好地优化它。

首先你需要一个IndexTable,我把它放在master数据库中,但如果你没有写入权限,请将它保存在你的数据库中。

看起来像这样:

Id
 0
 1
 2
...
n

n对于所有场景来说都是足够大的数字,100,000是好的,1,000,000是更好的,10,000,000甚至更好。当然,列id是集群索引。

我不打算直接将它与你的查询联系起来,因为我没有真正理解它而且我懒得去解决它。

相反,我会将它与这个名为Transactions的表联系起来,我们希望汇总每天(或周或月等)发生的所有交易:

Date                       Amount
2012-18-12 04:58:56.453       10
2012-18-12 06:34:21.456      100
etc

以下查询将按天汇总数据

SELECT  i.Id, SUM(t.Amount) AS DailyTotal
FROM    IndexTable i
        INNER JOIN
        Transactions t ON i.Id=DATEDIFF(DAY, 0, t.Date)
GROUP BY i.Id

DATEDIFF函数返回两个日期之间的日期部分数,在这种情况下是1900-01-01 0:00:00.000(SQL Server中的DateTime = 0)和日期之间的天数。交易(从那时起已经有41,261天 - 看看为什么我们需要一张大桌子)

同一天的所有交易都会有相同的号码。更改为周或月或秒(非常大的数字)就像更改日期部分一样简单。

如果它比您感兴趣的数据更早,您可以在此之后输入一个更新的开始日期,但它会使性能无差异。

我在这里使用过INNER JOIN所以如果某一天没有交易,那么我们就没有行,但LEFT JOIN会将这些空日期作为总计NULL给出(如果你想获得0,请使用ISNULL语句。

使用标准化数据,您可以根据需要PIVOT获取您要查找的输出。