如何改进简单但低效的父/子关系查询

时间:2017-12-09 02:03:17

标签: sql sql-server tsql

我有一个如下所述的简单表格,其中可能存在父/子关系。这实际上是一个非常大的表,但这是一个公平的表示。没有“孙子”关系。

我需要将其转换为稍微不同的表格,该表格会根据某些输入值进行过滤。

    declare @pc table ( myId char(1) not null, parentId char(1)  );

    insert into @pc (myId, parentId) values ('A', null)
    insert into @pc (myId, parentId) values ('B', 'A')
    insert into @pc (myId, parentId) values ('C', 'A')
    insert into @pc (myId, parentId) values ('D', null)
    insert into @pc (myId, parentId) values ('E', null)
    insert into @pc (myId, parentId) values ('F', 'E')
    insert into @pc (myId, parentId) values ('G', null)
    insert into @pc (myId, parentId) values ('H', 'G')
    insert into @pc (myId, parentId) values ('I', 'G')
    insert into @pc (myId, parentId) values ('J', 'G')
    insert into @pc (myId, parentId) values ('K', null)

    -- This is the results I need
    declare @target table ( myId char(1) not null, parentId char(1), hasFamily bit );

如果输入“A”,那么我需要三行:

    A  NULL 1
    B  A    1
    C  A    1

换句话说,我需要属于“家庭群体”的所有东西。

鉴于“B”我需要相同的输出,B的家庭组中的所有内容(恰好是A的):

    A  NULL 1
    B  A    1
    C  A    1

鉴于“D”,我只需要一行,因为没有人在D家庭中:

    D NULL  0

鉴于null,我需要整个表数据集,但是正确的行标记为“有家人”。

这是我的尝试,这在技术上是正确的,但是根据数据进行3次传递并不高效:

    declare @testcase char(1) = 'B';

    -- The inefficient method
    INSERT INTO @target( myId,parentId )
          SELECT pc.myId, pc.parentId
          FROM @pc pc
          WHERE(pc.myId = ISNULL(@testcase, pc.myId))
              OR (pc.parentId =@testcase);

    INSERT INTO @target( myId,parentId )
          SELECT pc.myId, pc.parentId
          FROM @pc pc
          WHERE pc.myId IN ( SELECT parentId FROM @target )
               AND pc.myId NOT IN  ( SELECT myId FROM @target );

    update t 
        set t.hasFamily = 1
        from @target t
        left outer join @target t2
        on t.myId = t2.parentId
        where t.parentId is not null or t2.myId is not null;

你能看到一个更好的方法来看待这个问题吗?

1 个答案:

答案 0 :(得分:1)

一个计算子项的窗口函数,在每一行都可用,简化了所需的查询。您是如何选择部署它的。例如,您可以选择使用持久计算列或触发器。如果规模和性能是问题,那么您需要考虑索引并检查执行计划。

也许我没有看到你在这里展示的小型模型的复杂性,因此我的建议可能过于简单。

SQL Fiddle

演示
create table Table1 ( myId char(1) not null, parentId char(1)  );

insert into Table1 (myId, parentId) values ('A', null);
insert into Table1 (myId, parentId) values ('B', 'A');
insert into Table1 (myId, parentId) values ('C', 'A');
insert into Table1 (myId, parentId) values ('D', null);
insert into Table1 (myId, parentId) values ('E', null);
insert into Table1 (myId, parentId) values ('F', 'E');
insert into Table1 (myId, parentId) values ('G', null);
insert into Table1 (myId, parentId) values ('H', 'G');
insert into Table1 (myId, parentId) values ('I', 'G');
insert into Table1 (myId, parentId) values ('J', 'G');
insert into Table1 (myId, parentId) values ('K', null);

create view too_simplistic as
select
       myId
     , parentId
     , coalesce(parentId, myId) parent_flat
     , case when count(*) over(partition by coalesce(parentId, myId))-1 = 0
                 then 0 
                 else 1
       end as hasFamily 
    from table1
; 

查询1

declare @want char(1) = 'A'

select myId, parentId, hasFamily
from too_simplistic
where parent_flat = (select parent_flat from too_simplistic where MyId = @want)
or MyId = @want

<强> Results

| myId | parentId | hasFamily |
|------|----------|-----------|
|    A |   (null) |         1 |
|    B |        A |         1 |
|    C |        A |         1 |

查询2

declare @want char(1) = 'B'

select myId, parentId, hasFamily
from too_simplistic
where parent_flat = (select parent_flat from too_simplistic where MyId = @want)
or MyId = @want

<强> Results

| myId | parentId | hasFamily |
|------|----------|-----------|
|    A |   (null) |         1 |
|    B |        A |         1 |
|    C |        A |         1 |

查询3

declare @want char(1) = 'D'

select myId, parentId, hasFamily
from too_simplistic
where parent_flat = (select parent_flat from too_simplistic where MyId = @want)
or MyId = @want

<强> Results

| myId | parentId | hasFamily |
|------|----------|-----------|
|    D |   (null) |         0 |

查询4

select *
from too_simplistic

<强> Results

| myId | parentId | parent_flat | hasFamily |
|------|----------|-------------|-----------|
|    A |   (null) |           A |         1 |
|    B |        A |           A |         1 |
|    C |        A |           A |         1 |
|    D |   (null) |           D |         0 |
|    E |   (null) |           E |         1 |
|    F |        E |           E |         1 |
|    G |   (null) |           G |         1 |
|    H |        G |           G |         1 |
|    I |        G |           G |         1 |
|    J |        G |           G |         1 |
|    K |   (null) |           K |         0 |



select d.*
     , case when children = 0 then 0 else 1 end as hasFamily 
from (
    select * 
         , count(*) over(partition by coalesce(parentId, myId))-1 children
    from @pc
    ) d
;