在自联接表

时间:2016-09-01 20:58:58

标签: tsql common-table-expression hierarchical-data recursive-query self-join

我在自连接分层表中有数据,其中大陆有多个国家有多个区域有多个状态有很多城市

自我加入表格结构:

|-------------------------------------------------------------|
| ID  | Name          |  Type       |  ParentID  | IsTopLevel |
|-------------------------------------------------------------|
| 1   | North America |  Continent  |  NULL      | 1          |
| 12  | United States |  Country    |  1         | 0          |
| 113 | Midwest       |  Region     |  12        | 0          |
| 155 | Kansas        |  State      |  113       | 0          |
| 225 | Topeka        |  City       |  155       | 0          |
| 2   | South America |  Continent  |  NULL      | 1          |
| 22  | Argentina     |  Country    |  2         | 0          |
| 223 | Southern      |  Region     |  22        | 0          |
| 255 | La Pampa      |  State      |  223       | 0          |
| 777 | Santa Rosa    |  City       |  255       | 0          |
|-------------------------------------------------------------|

我已经能够成功使用递归CTE来获取每个节点的树结构和深度。我失败的地方是使用一个支点创建一个很好的列表,列出所有底层位置及其相应的父母在每个级别。

预期结果:

|------------------------------------------------------------------------------------|
| Continent     | Country       | Region   | State    | City       | Bottom_Level_ID |
|------------------------------------------------------------------------------------|
| North America | United States | Midwest  | Kansas   | Topeka     | 234             |
| South America | Argentina     | Southern | La Pampa | Santa Rosa | 777             |
|------------------------------------------------------------------------------------|

我应该澄清一些关键点。

  1. 每个条目都有一个底层和一个顶层。没有 在给定位置不存在所有五种类型的情况。

  2. 如果我填写了这些数据,我在北美有50个条目 州级,所以你可以想象这张桌子有多么壮观 这个星球上每个大陆的城市级别。 数十亿行。

  3. 这是必要的原因是因为我需要能够加入一个人住过的所有地址的历史表,然后走上树。我想如果我从该表中获得LocationID,我可以将LEFT JOIN挂到查询的视图上并找到相应的列。

  4. 这是一个旧数据库,2005年,我没有系统管理员或模式控制。

  5. 我的CTE代码

    --CTE
    ;WITH Tree
    AS (
      SELECT ID, Name, ParentID, Type, 1 as Depth
      FROM LocationTable
      WHERE IsTopLevel = 1
      UNION ALL
      SELECT L.ID, L.Name, L.ParentID, L.Type, T.Depth+1
      FROM Tree T
        JOIN LocationTable L
          ON L.ParentGUID = T.GUID
    )
    

    良好的可靠数据,采用最有用的格式。然后我开始思考它,并且表格结构已经不是这种格式了,那么如果我不能同时加入条目,为什么还要进行深度树搜索呢?

    无论如何,剩下的就是这里。

    数据透视尝试

    ;WITH Tree
    AS (
      SELECT ID, Name, ParentID, Type
      FROM LocationTable
      WHERE IsTopLevel = 1
      UNION ALL
      SELECT L.ID, L.Name, L.ParentID, L.Type
      FROM Tree T
        JOIN LocationTable L
          ON L.ParentGUID = T.GUID
    )
    select *
    from Tree
    pivot (
        max(Name)
        for Type in ([Continent],[Country],[Region],[State],[City])
    ) pvt
    

    现在我在列中输入了所有内容,其他所有内容都为空。正如我以前一直在努力,我需要在尝试我​​的枢轴之前过滤/加入CTE数据,但我不知道从哪一步开始。我尝试的一切都是soooooooooo sloooooooow。

    每次我认为我理解CTE和Pivot,一些新的东西让我非常谦卑。请帮我。 ; ;

2 个答案:

答案 0 :(得分:4)

如果您的结构与您描述的一样干净(没有间隙,总是5个级别),您可能会采用简单方法:

这些数据确实需要经典的 1:n-table-tree ,其中您的国家/地区,州等都位于自己的表中并链接到其父记录

确保有关于ParentID和ID的索引!

DECLARE @tbl TABLE(ID INT,Name VARCHAR(100),Type VARCHAR(100),ParentID INT,IsTopLevel BIT);
INSERT INTO @tbl VALUES
 (1,'North America','Continent',NULL,1)
,(12,'United States','Country',1,0)
,(113,'Midwest','Region',12,0)
,(155,'Kansas','State',113,0)
,(225,'Topeka','City',155,0)
,(2,'South America','Continent',NULL,1)
,(22,'Argentina','Country',2,0)
,(223,'Southern','Region',22,0)
,(255,'La Pampa','State',223,0)
,(777,'Santa Rosa','City',255,0);

SELECT Level1.Name AS Continent
      ,Level2.Name AS Country
      ,Level3.Name AS Region
      ,Level4.Name AS State
      ,Level5.Name AS City
      ,Level5.ID AS Bottom_Level_ID
FROM @tbl AS Level1 
    INNER JOIN @tbl AS Level2 ON Level1.ID=Level2.ParentID
        INNER JOIN @tbl AS Level3 ON Level2.ID=Level3.ParentID
            INNER JOIN @tbl AS Level4 ON Level3.ID=Level4.ParentID
                INNER JOIN @tbl AS Level5 ON Level4.ID=Level5.ParentID
WHERE Level1.ParentID IS NULL

结果

Continent       Country         Region      State       City      Bottom_Level_ID
North America   United States   Midwest     Kansas      Topeka      225
South America   Argentina       Southern    La Pampa    Santa Rosa  777

答案 1 :(得分:0)

CTE的另一个解决方案可能是:

;WITH Tree
AS (
  SELECT cast(NULL as varchar(100)) as C1, cast(NULL as varchar(100)) as C2, cast(NULL as varchar(100)) as C3, cast(NULL as varchar(100)) as  C4, Name as C5, ID as B_Level
  FROM LocationTable
  WHERE IsTopLevel = 1
  UNION ALL
  SELECT T.C2, T.C3, T.C4, T.C5, L.Name, L.ID
  FROM Tree T
    JOIN LocationTable L
      ON L.ParentID = T.B_Level
)
select *
from Tree
where C1 is not null