【问题标题】:Complex grouping - design / performance problem复杂分组 - 设计/性能问题
【发布时间】:2011-10-24 11:03:34
【问题描述】:

警告:这是一个大问题


我有一个设计问题,一开始很简单,但一步步发展就完全难住了我。

现实的简单版本有一个漂亮的平面事实表...
所有名称都已更改以保护无辜

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 值 - 总是 - 包含在每一行中,因为 NULL - 意味着 - “适用于所有内容”。数据看起来像......

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


虽然解释起来很混乱,但在我的脑海中却是完整且合乎逻辑的。我理解被问到的问题,但在我的一生中,我似乎无法为此编写一个不需要花费大量时间来执行的查询。

那么,您将如何编写这样的查询和/或重构架构?

我很感激人们会询问我迄今为止所做的事情,但我很想先听听其他人的纯正想法和建议;)

【问题讨论】:

  • 从表面上看,主表似乎没有正确规范化。这是否适用于键/值对?
  • 可能。我尝试将它存储在 E-A-V 结构中,其中 E 实际上是多个列(层),A 是度量“ID”。就我写的结果查询而言;他们还是猪。

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


【解决方案1】:

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

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.

对此的查询应该“不会太难”。

【讨论】:

  • 嗨,我想我可能对我的示例造成了一些混淆 :) “活动”只是用于示例的一层层次结构。但是将层重构为递归定义的固定深度树结构的想法可能有好处。睡了一会儿我会试试的:)
  • 同意,问题 1 是规范化(或部分使用)。我几乎觉得这是创建(预定义格式)报告时使用的临时表,而不是基表。 @Dems 将表修改为更高的规范化形式时,尽量不要存储派生字段(这里似乎比我最初注意到的要多)。 (推迟自己的答案,直到 OP 显示下一个修订版)。
猜你喜欢
  • 1970-01-01
  • 2011-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多