复杂分组 - 设计/性能问题

时间:2011-08-10 20:50:22

标签: sql data-structures sql-server-ce aggregation

警告:这是一个大问题


我的设计问题很简单,但在增长的一个步骤中我完全难以理解。

现实的简单版本有一个很好的扁平事实表...
所有名称都已更改,以保护无辜的

CREATE TABLE raw_data (
  tier0_id INT, tier1_id  INT, tier2_id INT, tier3_id INT,
  metric0  INT, metric1   INT, metric2  INT, metric3  INT
)

tierID与固定深度树中的实体相关。比如业务层次结构。

指标只是表现数据,例如捕获的青蛙数量或释放的鸽子。

在报告中,善意的用户会做出类似以下内容的选择:

  • tier0_id的34和55 - 单独显示
  • 所有tier1_id - 分组在一起
  • 所有tier2_id - 分组在一起
  • 所有tier3_id - 单独显示
  • 指标2和3

这给了我以下类型的查询:

SELECT
  CASE WHEN @t0_grouping = 1 THEN NULL ELSE tier0_id END AS tier0_id,
  CASE WHEN @t1_grouping = 1 THEN NULL ELSE tier1_id END AS tier1_id,
  CASE WHEN @t2_grouping = 1 THEN NULL ELSE tier2_id END AS tier2_id,
  CASE WHEN @t3_grouping = 1 THEN NULL ELSE tier3_id END AS tier3_id,
  SUM(metric2) AS metric2, SUM(metric3) AS metric3
FROM
  raw_data
INNER JOIN tier0_values ON tier0_values.id = raw_data.tier0_id OR tier0_values.id IS NULL
INNER JOIN tier1_values ON tier1_values.id = raw_data.tier1_id OR tier1_values.id IS NULL
INNER JOIN tier2_values ON tier2_values.id = raw_data.tier2_id OR tier2_values.id IS NULL
INNER JOIN tier3_values ON tier3_values.id = raw_data.tier3_id OR tier3_values.id IS NULL
GROUP BY
  CASE WHEN @t0_grouping = 1 THEN NULL ELSE tier0_id END,
  CASE WHEN @t1_grouping = 1 THEN NULL ELSE tier1_id END,
  CASE WHEN @t2_grouping = 1 THEN NULL ELSE tier2_id END,
  CASE WHEN @t3_grouping = 1 THEN NULL ELSE tier3_id END

这是动态SQL和参数化查询的完美组合。是的,我知道,但SQL-CE让人们做了一些奇怪的事情。此外,当以下变更纳入时,可以整理出来......


从现在开始,我们需要能够在不同的层中包含NULL。这意味着“适用于该层中的所有实体”。

例如,使用以下非常简化的数据:

Activity    WorkingTime    ActiveTime    BusyTime

   1            0m             10m          0m
   2            0m             15m          0m
   3            0m             20m          0m
  NULL         60m              0m         45m

WorkingTime永远不会应用于活动,因此值会带有NULL ID。但ActiveTime特别关注特定活动,因此它使用合法ID。 BusyTime也反对NULL活动,因为它是所有ActiveTime的累积。

如果要报告此数据,则NULL值-always-会包含在每一行中,因为NULL -means-“适用于所有内容”。数据看起来像......

Activity    WorkingTime    ActiveTime    BusyTime   (BusyOnOtherActivities)

   1           60m             10m         45m            (45-10 = 35m)
   2           60m             15m         45m            (45-15 = 30m)
   3           60m             20m         45m            (45-20 = 25m)

  1&2          60m             25m         45m            (45-25 = 20m)
  1&3          60m             30m         45m            (45-30 = 15m)
  2&3          60m             35m         45m            (45-35 = 10m)

  ALL          60m             45m         45m            (45-45 =  0m)



希望这个例子有意义,因为它实际上是一个多层次的层次结构(根据原始示例),并且在每个层中都允许NULL。所以我将尝试一个3层的例子......

t0_id  |  t1_id  |  t2_id   |   m1  |  m2  |  m3  |  m4  |  m5
    1         3        10   |    0     10      0      0      0
    1         4        10   |    0     15      0      0      0
    1         5        10   |    0     20      0      0      0
    1      NULL        10   |   60      0     45      0      0
    2         3        10   |    0      5      0      0      0
    2         5        10   |    0     10      0      0      0
    2         6        10   |    0     15      0      0      0
    2      NULL        10   |   50      0     30      0      0
    1         3        11   |    0      7      0      0      0
    1         4        11   |    0      8      0      0      0
    1         5        11   |    0      9      0      0      0
    1      NULL        11   |   30      0     24      0      0
    2         3        11   |    0      8      0      0      0
    2         5        11   |    0     10      0      0      0
    2         6        11   |    0     12      0      0      0
    2      NULL        11   |   40      0     30      0      0
 NULL      NULL        10   |    0      0      0     60      0
 NULL      NULL        11   |    0      0      0     60      0
 NULL      NULL      NULL   |    0      0      0      0      2

这会在报告中提供许多可能不同的输出记录,但这里有几个例子......

t0_id  |  t1_id  |  t2_id   |   m1  |  m2  |  m3  |  m4  |  m5

    1         3        10   |   60     10     45     60      2
    1         4        10   |   60     15     45     60      2
    1         5        10   |   60     20     45     60      2

    2         3        10   |   50      5     30     60      2
    2         5        10   |   50     10     30     60      2
    2         6        10   |   50     15     30     60      2

    1       ALL        10   |   60     45     45     60      2
    2       ALL        10   |   50     30     30     60      2

  ALL         3        10   |  110     15     75     60      2
  ALL         4        10   |   60     15     45     60      2
  ALL         5        10   |  110     30     75     60      2
  ALL         6        10   |   50     15     30     60      2

  ALL         3       ALL   |  180     30    129    120      2
  ALL         4       ALL   |   90     23     69    120      2
  ALL         5       ALL   |  180     49    129    120      2
  ALL         6       ALL   |   90     27     60    120      2

  ALL       ALL        10   |  110    129    129     60      2
  ALL       ALL        11   |   70    129    129     60      2
  ALL       ALL       ALL   |  180    129    129    120      2

    1       3&4       ALL   |   90     40     69    120      2
  ALL       3&4       ALL   |  180     53    129    120      2


虽然这很难解释,但它在我的头脑中具有完整和合乎逻辑的意义。我理解被问到的是什么,但对于我的生活,我似乎无法为此写一个不需要花费大量时间来执行的查询。

那么,您将如何编写此类查询和/或重构架构?

我感谢人们会询问我到目前为止所做的事情的例子,但我很想先听听其他人的未经破坏的想法和建议;)

1 个答案:

答案 0 :(得分:0)

问题看起来更像是规范化活动。我将从规范化表开始 类似于:(根据您的使用情况,您可能需要更多的身份字段)

CREATE TABLE raw_data (
    rawData_ID INT,
    Activity_id INT, 
    metric0  INT)

我创建了一个类似于以下内容的分层表:( tierplan允许多个分组。如果tier_id没有父代可以汇总,那么tierparent_id为NULL这将在查询中进行递归。)

CREATE TABLE tiers (
    tierplan_id INT,
    tier_id INT,
    tierparent_id INT)

最后,我创建了一个与层和活动相关的表,如:

CREATE TABLE ActivTiers (
    Activplan_id INT, --id on the table
    tierplan_id INT,  --tells what tierplan the raw_data falls under
    rawdata_id INT)   --this allows the ActivityId to be payload instead of identifier.

对此的质疑应该“不太难”。