2015-02-06 85 views
1

我有一个这样的表结构的共同祖先(其实有更多的层次):找到一个树形结构表

------------------------------------------ 
|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  |  |  | 
------------------------------------------ 

我真的不知道如何攻击这个问题。我想通过邮编分区,但这仍然留下找到每个分区内共同的祖先的问题...

+2

一个树状结构表是将每一行都有它的父行的引用的表。我不知道你在那里做什么,我不明白你用'a => x => i''表示的意思。另外,根据我的经验,无论何时我遇到具有名称列相似列的表仅有一个(数字或非数字)后缀的表时,某个人在某个地方不理解数据库规范化。 – 2015-02-06 15:49:18

+0

这当然不是标准化的,我意识到这一点。我可能会这样做,但如果有解决方案,我会感兴趣。 'a => x => i'表示它是示例表中的第一行(region1 = a,region2 = x,region3 = i) – 2015-02-06 15:52:18

+1

查看https://msdn.microsoft.com/zh-cn/ gb/library/bb677173%28v = sql.110%29.aspx – Elliveny 2015-02-06 15:54:59

回答

2

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

-- 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 

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

+0

非常有用 - 我认为我可以修改这些以用于我的真实数据......关于其他评论,我知道这很麻烦,我应该首先以不同的方式构造数据。所以,作为一个重要的说明,我会接受这个答案。谢谢! – 2015-02-06 16:41:38

1

这个问题,而吸引了我,我想出了一个替代方案,这可能对您有用:

-- 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