在树状结构中查找共同祖先 table

Find the common ancestor in a tree-structured table

我有一个这样的table结构(实际上还有更多层级):

------------------------------------------
|region1|region2|region3|region4|postcode|
|-------|-------|-------|-------|--------|
|a      |x      |i      |       |1       |
|a      |y      |i      |       |2       |
|a      |y      |j      |       |2       |
|a      |z      |k      |       |3       |
|b      |u      |m      |       |4       |
|b      |       |n      |       |4       |
|c      |       |       |       |5       |
|c      |q      |       |       |6       |
------------------------------------------

因此,例如,a => x => ia => y => i 是不同的地方,但都在同一区域1 a

我想知道每个邮政编码可以覆盖哪个地区。

例如,代码 2 涵盖区域 a => y => ia => y => j,因此它们的共同祖先是 a => y.

这里是示例中查询 运行 的期望输出:

------------------------------------------
|postcode|region1|region2|region3|region4|
|--------|-------|-------|-------|-------|
|1       |a      |x      |i      |       |
|2       |a      |y      |       |       |
|3       |a      |z      |k      |       |
|4       |b      |       |       |       |
|5       |c      |       |       |       |
|6       |c      |q      |       |       |
------------------------------------------

我真的不知道如何解决这个问题。我考虑过按邮政编码进行分区,但这仍然存在在每个分区中找到共同祖先的问题...

这是一个非常混乱的解决方案,但它似乎给出了正确的答案。毫无疑问,它需要相当多的工作才能满足您的实际要求,但也许这可以帮助您指明某种方向!

-- Setup a test table

DECLARE @tbl AS TABLE(R1 NVARCHAR(10), R2 NVARCHAR(10), R3 NVARCHAR(10), R4 NVARCHAR(10), PC NVARCHAR(10));

INSERT INTO @tbl(R1,R2,R3,R4,PC) VALUES ('a','x','i',NULL,'1');
INSERT INTO @tbl(R1,R2,R3,R4,PC) VALUES ('a','y','i',NULL,'2');
INSERT INTO @tbl(R1,R2,R3,R4,PC) VALUES ('a','y','j',NULL,'2');
INSERT INTO @tbl(R1,R2,R3,R4,PC) VALUES ('a','z','k',NULL,'3');
INSERT INTO @tbl(R1,R2,R3,R4,PC) VALUES ('b','u','m',NULL,'4');
INSERT INTO @tbl(R1,R2,R3,R4,PC) VALUES ('b',NULL,'n',NULL,'4');
INSERT INTO @tbl(R1,R2,R3,R4,PC) VALUES ('c',NULL,NULL,NULL,'5');
INSERT INTO @tbl(R1,R2,R3,R4,PC) VALUES ('c','q',NULL,NULL,'6');

-- Calculate the result:

SELECT 
    PC,
    CASE WHEN LVL1 = 1 THEN R1 ELSE NULL END AS R1,
    CASE WHEN LVL2 = 1 THEN R2 ELSE NULL END AS R2,
    CASE WHEN LVL3 = 1 THEN R3 ELSE NULL END AS R3,
    CASE WHEN LVL4 = 1 THEN R4 ELSE NULL END AS R4
FROM
(
    SELECT
      PC,
      MAX(R1) AS R1, 
      MAX(R2) AS R2,
      MAX(R3) AS R3, 
      MAX(R4) AS R4,
      COUNT(DISTINCT ISNULL(R1,'.')) AS LVL1, 
      COUNT(DISTINCT ISNULL(R1,'.') + ISNULL(R2,'.')) AS LVL2, 
      COUNT(DISTINCT ISNULL(R1,'.') + ISNULL(R2,'.') + ISNULL(R3,'.')) AS LVL3,
      COUNT(DISTINCT ISNULL(R1,'.') + ISNULL(R2,'.') + ISNULL(R3,'.') + ISNULL(R4,'.')) AS LVL4
    FROM @tbl
    GROUP BY PC
) A

最终结果与问题中的 table 匹配。

这个问题让我很感兴趣,我想出了一个替代方案,您可能会发现它有用:

-- Setup test table
DECLARE @InputTable TABLE (region1 varchar(2), region2 varchar(2), region3 varchar(2), region4 varchar(2), postcode varchar(2))

INSERT INTO @InputTable (region1, region2, region3, region4, postcode)
          SELECT 'a','x','i',null,'1' 
UNION ALL SELECT 'a','y','i',NULL,'2'
UNION ALL SELECT 'a','y','j',NULL,'2'
UNION ALL SELECT 'a','z','k',NULL,'3'
UNION ALL SELECT 'b','u','m',NULL,'4'
UNION ALL SELECT 'b',NULL,'n',NULL,'4'
UNION ALL SELECT 'c',NULL,NULL,NULL,'5'
UNION ALL SELECT 'c','q',NULL,NULL,'6'

-- Find the common ancestors
;with totals as (
  select postcode, count(*) as postcodeCount from @InputTable group by postcode
)
, region4group as (
  select postcode, region1, region2, region3, region4 from @InputTable in1 
  group by postcode, region1, region2, region3, region4 having count(*)=(select postCodeCount from totals where totals.postcode=in1.postcode)
)
, region3group as (
  select * from region4group
  union
  select in1.postcode, in1.region1, in1.region2, in1.region3, null from @InputTable in1 
  left outer join region4group on region4group.postcode=in1.postcode
  where region4group.postcode is null
  group by in1.postcode, in1.region1, in1.region2, in1.region3 
  having count(*)=(select postCodeCount from totals where totals.postcode=in1.postcode)
)
, region2group as (
  select * from region3group
  union
  select in1.postcode, in1.region1, in1.region2, null, null from @InputTable in1
  left outer join region3group on region3group.postcode=in1.postcode
  where region3group.postcode is null
  group by in1.postcode, in1.region1, in1.region2 
  having count(*)=(select postCodeCount from totals where totals.postcode=in1.postcode)
)
, commonancestors as (
  select * from region2group
  union
  select in1.postcode, in1.region1, null, null, null from @InputTable in1 
  left outer join region2group on region2group.postcode=in1.postcode
  where region2group.postcode is null
  group by in1.postcode, in1.region1 
  having count(*)=(select postCodeCount from totals where totals.postcode=in1.postcode)
)
select * from commonancestors