如何在T-SQL中以分层格式高效地分组数据?

如何在T-SQL中以分层格式高效地分组数据?,sql,sql-server,sql-server-2005,tsql,Sql,Sql Server,Sql Server 2005,Tsql,我有这样的数据: Task | Hours 1.1 | 40 2 | 40 2.1 | 60 2.1.1 | 15 15.9 | 24 16 | 5 19.1 | 40 19.1.1 | 8 19.1.2 | 12 19.2 | 6 19.2.1 | 21 19.2.2 | 15 19.2.3 | 2 19.3 | 64 我想根据任务的前两个级别进行分

我有这样的数据:

Task   | Hours
1.1    |    40
2      |    40
2.1    |    60
2.1.1  |    15
15.9   |    24
16     |     5
19.1   |    40
19.1.1 |     8
19.1.2 |    12
19.2   |     6
19.2.1 |    21
19.2.2 |    15
19.2.3 |     2
19.3   |    64
我想根据任务的前两个级别进行分组,得出以下结果:

Task   | Hours
1.1    |    40
2      |    40
2.1    |    75
15.9   |    24
16     |     5
19.1   |    60
19.2   |    44
19.3   |    64

我不想让16个关卡卷起它下面的东西,但我需要所有其他关卡卷起。这是SQLServer2005。我通常会在小数点上进行拆分,并以这种方式进行拆分,但我想知道是否有更好的方法在SQL中进行拆分。

假设字段任务的结构是一致的,您可以使用以下方法

select left(task,4) as Task,sum(hours) as Hours
from table
group by left(task,4)
这是一个稍微修改过的版本

select LEFT(task,charindex('.',task+'.')+1),SUM(hours)
from test1
group by LEFT(task,charindex('.',task+'.')+1)

假设现场任务的结构是一致的,您可以使用以下方法

select left(task,4) as Task,sum(hours) as Hours
from table
group by left(task,4)
这是一个稍微修改过的版本

select LEFT(task,charindex('.',task+'.')+1),SUM(hours)
from test1
group by LEFT(task,charindex('.',task+'.')+1)

改变模型是一种选择吗?如果您的任务列实际上是要表示层次结构,那么您应该在关系模型中正确地表示层次结构

如果深度级别的数量固定为三,另一个选项可能是添加三列,分别表示任务列的每个“部分”

如果这不是一个选项,我认为您可以通过一系列CASE语句来解析字符串(加上SUM和groupby)来实现这一点

更新:

好吧,这似乎是一个有趣的挑战,所以我想出了这个:

SELECT
    main_task,
    SUM(hours)
FROM
    (
    SELECT      
        task,
        CASE 
            WHEN 
                LEN(task) + 1 - CHARINDEX('.', REVERSE(task)) = CHARINDEX ('.', task) THEN task
                ELSE LEFT(task, LEN(task) + 1 - CHARINDEX('.', REVERSE(task)) - 1)
            END main_task,
        hours
    FROM 
        #temp
    ) sub
GROUP BY 
      main_task

改变模型是一种选择吗?如果您的任务列实际上是要表示层次结构,那么您应该在关系模型中正确地表示层次结构

如果深度级别的数量固定为三,另一个选项可能是添加三列,分别表示任务列的每个“部分”

如果这不是一个选项,我认为您可以通过一系列CASE语句来解析字符串(加上SUM和groupby)来实现这一点

更新:

好吧,这似乎是一个有趣的挑战,所以我想出了这个:

SELECT
    main_task,
    SUM(hours)
FROM
    (
    SELECT      
        task,
        CASE 
            WHEN 
                LEN(task) + 1 - CHARINDEX('.', REVERSE(task)) = CHARINDEX ('.', task) THEN task
                ELSE LEFT(task, LEN(task) + 1 - CHARINDEX('.', REVERSE(task)) - 1)
            END main_task,
        hours
    FROM 
        #temp
    ) sub
GROUP BY 
      main_task

另一种方法是添加一些计算列,将各个任务级别分开,然后根据需要分组和求和。

另一种方法是添加一些计算列,将各个任务级别分开,然后根据需要分组和求和。

我在回家的路上考虑过这个问题,我想提出以下解决方案:

创建一个存储层次结构的表,然后执行一个获取任务父级的联接

TaskStructureTable:

task  | task_group
1     | 1
1.1   | 1.1
1.1.1 | 1.1
1.1.2 | 1.1
1.1.3 | 1.1
1.2   | 1.2
1.2.1 | 1.2
然后我可以这样做:

SELECT SUM(d.Hours) AS "Hours", t.task_group
FROM Data d
JOIN TaskStructureTable t ON d.Task = t.task

你认为这比做CHARINDEX要快吗?(是的,我可以测量并确定)

我在开车回家的路上考虑过这个问题,我想提出这个解决方案:

创建一个存储层次结构的表,然后执行一个获取任务父级的联接

TaskStructureTable:

task  | task_group
1     | 1
1.1   | 1.1
1.1.1 | 1.1
1.1.2 | 1.1
1.1.3 | 1.1
1.2   | 1.2
1.2.1 | 1.2
然后我可以这样做:

SELECT SUM(d.Hours) AS "Hours", t.task_group
FROM Data d
JOIN TaskStructureTable t ON d.Task = t.task

你认为这比做CHARINDEX要快吗?(是的,我可以测量并肯定知道)

如果中间部分超过一个数字,我认为您的更新答案仍然会失败?如果你能确定中间部分的长度并用该值替换“+1”,我想你已经知道了。如果中间部分超过一个数字,你的更新答案仍然会失败?如果你能确定中间部分的长度,并用该值替换“+1”,我认为你已经做到了。我认为如果你的建议符合你的要求,你几乎可以将任务组视为一个“类别”,而不是层次结构的一部分。您甚至不需要单独的表,尽管我建议使用一个表来实现引用完整性(有点像类别查找)。是的,以这种方式构造它并在类别(_id)上放置索引肯定比解析字符串性能更好。如果要更改模型,并且使用sql 2008,则可以研究使用hierarchyid数据类型。这将使在任何级别分解报告变得非常简单。SQL Server 2005不幸的是,我认为如果您的建议符合您的要求,您几乎可以将任务组视为一个“类别”,而不是层次结构的一部分。您甚至不需要单独的表,尽管我建议使用一个表来实现引用完整性(有点像类别查找)。是的,以这种方式构造它并在类别(_id)上放置索引肯定比解析字符串性能更好。如果要更改模型,并且使用sql 2008,则可以研究使用hierarchyid数据类型。这将使在任意级别分解报表变得非常简单。SQL Server 2005不幸的是,我认为在模型中存储层次结构是正确的。我发布了一种可能的方法——这与您的想法一致吗?我认为您在模型中存储层次结构的想法是正确的。我发布了一个可能的方法——这是否符合你的想法?