title: Flatten hierarchy on self-join table
body: <p>I have data in a self-join hierarchical table where <strong>Continents</strong> have many <strong>Countries</strong> have many <strong>Regions</strong> have many <strong>States</strong> have many <strong>Cities</strong>.</p>
  
  <p><strong>Self-joining table structure:</strong></p>
  
  <pre><code>|-------------------------------------------------------------|
  | 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          |
  |-------------------------------------------------------------|
  </code></pre>
  
  <p>I have been able to successfully use a recursive CTE to get the tree structure and depth of each node. Where I am failing is using a pivot to create a nice list of all bottom locations and their corresponding parents at each level.</p>
  
  <p><strong>The expected results:</strong></p>
  
  <pre><code>|------------------------------------------------------------------------------------|
  | 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             |
  |------------------------------------------------------------------------------------|
  </code></pre>
  
  <p>There are a few key points I should clarify.</p>
  
  <ol>
  <li><p>Every single entry has a bottom level and a top level. There are no
  cases where all five Types are not present for a given location.</p></li>
  <li><p>If I filled out this data, I'd have 50 entries for North America at the
  State level, so you can imagine how immense this table is at the
  City level for every continent on the planet. <em>Billions</em> of rows.</p></li>
  <li><p>The reason this is a necessity is because I need to be able to join onto a historical table of all addresses a person has lived at, and journey up the tree. I figure if I have the LocationID from that table, I can just LEFT JOIN onto a View of this query and nab the appropriate columns.</p></li>
  <li><p>This is an old database, 2005, and I don't have sysadmin or control of the schema.</p></li>
  </ol>
  
  <p><strong>My CTE Code</strong></p>
  
  <pre><code>--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
  )
  </code></pre>
  
  <p>Good solid data, in a mostly useful format. BUT then I got to thinking about it and isn't the table structure already in this format, so why would I bother doing a depth tree search if I wasn't going to join the entries together at the same time?</p>
  
  <p>Anyway, here was the rest.</p>
  
  <p><strong>The Pivot Attempt</strong></p>
  
  <pre><code>;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
  </code></pre>
  
  <p>And now I have everything by Type in a column, with nulls for everything else. As I have struggled with before, I need to filter/join the CTE data before I attempt my pivot, but I have no idea where to start with that piece. Everything I have tried is soooooooooo sloooooooow.</p>
  
  <p>Everytime I think I understand CTEs and Pivot, something new makes me extremely humbled. Please help me. ; ;</p>