Help with recursive CTE query joining to a second table - sql

My objective is to recurse through table tbl and while recursing through that table select a country abbreviation (if it exists) from another table tbl2 and append those results together which are included in the final output.
The example I'll use will come from this post
tbl2 has a Foreign Key 'tbl_id' to tbl and looks like this
INSERT INTO #tbl2( Id, Abbreviation, tbl_id )
VALUES
(100, 'EU', 1)
,(101, 'AS', 2)
,(102, 'DE', 3)
,(103, 'CN', 5)
*Note: not all the countries have abbreviations.
The trick is, I want all the countries in Asia to at least show the abbreviation of Asia which is 'AS' even if a country doesn't have an abbreviation (like India for example). If the country does have an abbreviation the result needs to look like this: China:CN,AS
I've got it partly working using a subquery, but India always returns NULL for the abbreviation. It's acting like if there isn't a full recursive path back to the abbreviation, then it returns null. Maybe the solution is to use a left outer join on the abbreviation table? I've tried for hours many different variations and the subquery is as close as I can get.
WITH abcd
AS (
-- anchor
SELECT id, [Name], ParentID,
CAST(([Name]) AS VARCHAR(1000)) AS "Path"
FROM #tbl
WHERE ParentId IS NULL
UNION ALL
--recursive member
SELECT t.id, t.[Name], t.ParentID,
CAST((a.path + '/' + t.Name + ':' +
(
select t2.abbreviation + ','
from #tbl2
where t.id = t2.id
)) AS VARCHAR(1000)) AS "Path"
FROM #tbl AS t
JOIN abcd AS a
ON t.ParentId = a.id
)
SELECT * FROM abcd
btw, I'm using sql server 2005 if that matters

Try this example, which will give you the output (1 sample row)
id Name ParentID Path abbreviation (No column name)
5 China 2 Asia/China CN,AS Asia/China:CN,AS
The TSQL being
DECLARE #tbl TABLE (
Id INT
,[Name] VARCHAR(20)
,ParentId INT
)
INSERT INTO #tbl( Id, Name, ParentId )
VALUES
(1, 'Europe', NULL)
,(2, 'Asia', NULL)
,(3, 'Germany', 1)
,(4, 'UK', 1)
,(5, 'China', 2)
,(6, 'India', 2)
,(7, 'Scotland', 4)
,(8, 'Edinburgh', 7)
,(9, 'Leith', 8)
;
DECLARE #tbl2 table (id int, abbreviation varchar(10), tbl_id int)
INSERT INTO #tbl2( Id, Abbreviation, tbl_id )
VALUES
(100, 'EU', 1)
,(101, 'AS', 2)
,(102, 'DE', 3)
,(103, 'CN', 5)
;WITH abbr AS (
SELECT a.*, isnull(b.abbreviation,'') abbreviation
FROM #tbl a
left join #tbl2 b on a.Id = b.tbl_id
), abcd AS (
-- anchor
SELECT id, [Name], ParentID,
CAST(([Name]) AS VARCHAR(1000)) [Path],
cast(abbreviation as varchar(max)) abbreviation
FROM abbr
WHERE ParentId IS NULL
UNION ALL
--recursive member
SELECT t.id, t.[Name], t.ParentID,
CAST((a.path + '/' + t.Name) AS VARCHAR(1000)) [Path],
isnull(nullif(t.abbreviation,'')+',', '') + a.abbreviation
FROM abbr AS t
JOIN abcd AS a
ON t.ParentId = a.id
)
SELECT *, [Path] + ':' + abbreviation
FROM abcd

Related

Custom hierarchy

I've a table source
idGeo GEO PARENTID
1 EMEA NULL
2 France 1
3 mIDCAPSfRANCE 2
4 Germany 1
5 France exl midcaps 2
6 Amercias NULL
7 US 6
The expected result of the hierarchy
I tried to do left join(self join) but I'm not able to get exactly as expected.
Here is a generic method regardless of the level of the hierarchy.
SQL
-- DDL and sample data population, start
DECLARE #tbl table (
idGeo INT IDENTITY PRIMARY KEY,
GEO VARCHAR(64),
PARENTID INT
);
insert into #tbl (GEO, PARENTID) values
( 'EMEA', NULL),
( 'France', 1),
( 'mIDCAPSfRANCE', 2),
( 'Germany', 1),
( 'France exl midcaps', 2),
( 'Amercias', NULL),
( 'US', 6);
-- DDL and sample data population, end
--SELECT * FROM #tbl;
WITH cte AS
(
-- Anchor query
SELECT idGEO, GEO, ParentID, 1 AS [Level]
, CAST('/' + GEO AS VARCHAR(1000)) AS XPath
FROM #tbl
WHERE ParentID IS NULL
UNION ALL
-- Recursive query
SELECT t.idGEO, t.GEO, t.ParentID, cte.[Level] + 1 AS [Level]
, CAST(cte.[XPath] + '/' + t.GEO AS VARCHAR(1000)) AS [XPath]
FROM #tbl AS t
INNER JOIN cte ON t.ParentID = cte.idGEO
WHERE t.ParentID IS NOT NULL
)
SELECT idGEO
, REPLICATE(' ',[Level]-1) + GEO AS GEOHierarchy
, GEO, ParentID, [Level], [XPath]
FROM cte
ORDER BY XPath;
Output
So if I understand you want to generate columns as the levels go on.
You cannot dynalically generate columns, SQL is a fixed column language.

Need query to join table on column with comma separated

Table1
ID
Notes
ReasonID
1
Test1
[11,12]
2
Test2
[13,14]
Table 2
Reasonid
Name
11
Other1
12
Other2
13
Other3
14
Other4
Result should look like this, where Notes column from Table1 should concat with Name column from Table2.
ID
Final_Notes
1
Test1,Other1,Other2
2
Test2,Other3,Other4
If you use SQL Server 2017+, you may try to parse the ReasonID column as JSON, use an appropriate JOIN and then aggregate with STRING_AGG().
Sample data:
SELECT *
INTO Table1
FROM (VALUES
(1, 'Test1', '[11,12]'),
(2, 'Test2', '[13,14]')
) t (ID, Notes, ReasonID)
SELECT *
INTO Table2
FROM (VALUES
(11, 'Other1'),
(12, 'Other2'),
(13, 'Other3'),
(14, 'Other4')
) t (ReasonID, Name)
Statement:
SELECT
ID,
FinalNotes = CONCAT(
Notes,
',',
(
SELECT STRING_AGG(t2.Name, ',') WITHIN GROUP (ORDER BY CONVERT(int, j.[key]))
FROM OPENJSON(ReasonID) j
-- Important, JOIN with possible implicit conversion
JOIN Table2 t2 ON j.[value] = t2.ReasonID
)
)
FROM Table1
Result:
ID
FinalNotes
1
Test1,Other1,Other2
2
Test2,Other3,Other4
db<>fiddle
Please try the following solution.
It will work starting from SQL Server 2012 onwards.
It is using the following:
XML/XQuery to tokenize comma separated list of values.
FOR XML PATH to compose FinalNotes comma separated list.
SQL
-- DDL and sample data population, start
DECLARE #Table1 TABLE(ID INT, Notes VARCHAR(60), ReasonID VARCHAR(60));
INSERT INTO #Table1(ID, Notes, ReasonID) VALUES
(1, 'Test1', '[11,12]'),
(2, 'Test2', '[13,14]');
DECLARE #Table2 TABLE(Reasonid INT, Name VARCHAR(60));
INSERT INTO #Table2(Reasonid, Name) VALUES
(11, 'Other1'),
(12, 'Other2'),
(13, 'Other3'),
(14, 'Other4');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = ',';
;WITH rs AS
(
SELECT ID, Notes, Name
FROM #Table1 AS t
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' +
REPLACE(REPLACE(REPLACE(ReasonID,'[',''),']',''), #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)) AS t1(c)
CROSS APPLY c.nodes('/root/r/text()') AS t2(x)
INNER JOIN #Table2 AS t3 ON t3.Reasonid = x.value('.', 'INT')
)
SELECT ID, CONCAT(Notes
, (SELECT #separator + c.Name AS [text()]
FROM rs AS c
WHERE c.ID = p.ID
FOR XML PATH(''))) AS FinalNotes
FROM rs AS p
GROUP BY ID, Notes;
Output
+----+---------------------+
| ID | FinalNotes |
+----+---------------------+
| 1 | Test1,Other1,Other2 |
| 2 | Test2,Other3,Other4 |
+----+---------------------+
use SUBSTRING(string, 2, LEN(string)-2) for deleting [] and Parsename to split based on comma and join and concat as follows
Your data
DECLARE #Table1 TABLE(
ID INTEGER NOT NULL,
Notes VARCHAR(60) NOT NULL,
ReasonID VARCHAR(60) NOT NULL
);
INSERT INTO #Table1(ID, Notes, ReasonID)
VALUES
(1, 'Test1', '[11,12]'),
(2, 'Test2', '[13,14]');
DECLARE #Table2 TABLE(
Reasonid INTEGER NOT NULL,
Name VARCHAR(60) NOT NULL
);
INSERT INTO #Table2(Reasonid, Name)
VALUES
(11, 'Other1'),
(12, 'Other2'),
(13, 'Other3'),
(14, 'Other4');
your query
SELECT id,
Concat(notes, ',', T2.name, ',', T3.name) FinalNotes
FROM (SELECT id,
notes,
Parsename(Replace(SUBSTRING(ReasonID, 2, LEN(ReasonID)-2), ',', '.'), 2) R1,
Parsename(Replace(SUBSTRING(ReasonID, 2, LEN(ReasonID)-2), ',', '.'), 1) R2
FROM #table1) T1
join #table2 T2
ON T1.R1 = T2.reasonid
join #table2 T3
ON T1.R2 = T3.reasonid
by using XML
DROP TABLE IF EXISTS #t -- temporary table
select t1.ID,t1.Notes, Name into #t -- temporary table
from
(
SELECT A.ID,a.Notes,
Split.a.value('.', 'VARCHAR(100)') AS String
FROM (SELECT ID, Notes,
CAST ('<M>' + REPLACE(SUBSTRING(ReasonID, 2, LEN(ReasonID)-2) , ',', '</M><M>') + '</M>' AS XML) AS String
FROM #Table1) AS A CROSS APPLY String.nodes ('/M') AS Split(a)) t1
join #Table2 t2 on t1.String=t2.Reasonid
---XML Path
SELECT ID,concat(notes,',',
STUFF((SELECT ', ' + CAST(name AS VARCHAR(10)) [text()]
FROM #t t1
WHERE t1.ID = t.ID
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ')) FinalNotes
FROM #t t
GROUP BY ID,notes

Displaying multiple select in a single cell

Please help me to solve the following issue .
consider i have two tables in a Database
1.employee 2.Details
In employee table data will be
eid ename level
1 x 9th
2 y 10th
In Address Table data will be
AId eid location Adreess_type
1 1 india permananet
2 1 US Temporary
3 2 Japan permananet
4 2 China Temporary
I need output in the below format
eid ename fulllocation
1 X INDIA -US
2 y Japan-CHINA
Try this:
SELECT
e.eid,
e.name,
GROUP_CONCAT(a.location SEPARATOR '-') AS fulllocation
FROM
employee as e
INNER JOIN address as a
ON e.eid = a.eid
GROUP BY
e.eid
select employee.eid, employee.ename, t.fulllocation
from employee
inner join (select eid, group_concat(location SEPARATOR '-') as fulllocation from Address group by eid) t
on employee.eid = t.eid
Consider that GROUP_CONCAT have some limitations, what is this and how can change (if needed) it? please check documentation for this.
DECLARE #t1 TABLE
(
eid int NOT NULL,
ename varchar(50),
level varchar(50)
)
DECLARE #t2 TABLE
(
aid int NOT NULL,
eid int,
location varchar(50),
address_type varchar(50)
)
INSERT INTO #t1 SELECT 1, 'x', '9th'
INSERT INTO #t1 SELECT 2, 'y', '10th'
INSERT INTO #t2 SELECT 1, 1, 'india', 'permanent'
INSERT INTO #t2 SELECT 2, 1, 'US', 'temporary'
INSERT INTO #t2 SELECT 3, 2, 'Japan', 'permanent'
INSERT INTO #t2 SELECT 4, 2, 'China', 'temporary'
SELECT * FROM #t1
SELECT * FROM #t2
SELECT t1.eid, t1.ename, t2.fullLocation
FROM #t1 AS t1
INNER JOIN (
SELECT eid, COUNT(*) AS noofrecs
, fullLocation = LTRIM(RTRIM(ISNULL(STUFF(
(
SELECT DISTINCT '-' + CAST(t2.location as nvarchar(max))
FROM #t2 t2
WHERE t1.eid = t2.eid
FOR XML PATH (''), TYPE).value('.', 'nvarchar(max)'
), 1, 1, ''), '')))
FROM #t2 as t1
GROUP BY eid
) AS t2
ON t1.eid = t2.eid
DECLARE #t1 TABLE
(
eid int NOT NULL,
ename varchar(50),
level varchar(50)
)
DECLARE #t2 TABLE
(
aid int NOT NULL,
eid int,
location varchar(50),
address_type varchar(50)
)
INSERT INTO #t1 SELECT 1, 'x', '9th'
INSERT INTO #t1 SELECT 2, 'y', '10th'
INSERT INTO #t2 SELECT 1, 1, 'india', 'permanent'
INSERT INTO #t2 SELECT 2, 1, 'US', 'temporary'
INSERT INTO #t2 SELECT 3, 2, 'Japan', 'permanent'
INSERT INTO #t2 SELECT 4, 2, 'China', 'temporary'
SELECT * FROM #t1
SELECT * FROM #t2
SELECT b.eid,b.ename
, STUFF((SELECT '_ ' + a.location FROM #t2 A
Where A.eid=B.eid FOR XML PATH('')),1,1,'') As fulllocation
From #t1 B
Group By b.eid,b.ename
In order to preserve the order of the locations, you could work along
SELECT
e.eid
, e.ename
, CONCAT_WS('-', p.location, t.location) AS fulllocation
FROM Employee e
JOIN Address p
ON e.eid = p.eid
AND p.address_type = 'permananet'
JOIN Address t
ON e.eid = t.eid
AND t.address_type = 'Temporary'
;
See it in action: SQL Fiddle.
Please comment if and as this requrires adjustment / further detail.

SQL Building Pathway using With Union in SQL Server

This is SQL Server Question
I have a set of categories, and their relationship results in nested categories.
I want to build a pathway keeping the relationship and build the SEF urls. Here is what I am looking for:
Category table:
ID, Name
1, Root
2, Cat1
3, Cat2
4, Cat1.1
5, Cat1.2
6, Cat2.1
7, Cat2,2
CategoryChild table: ParentCategoryID, ChildCategoryID
1, 2
1, 3
2, 4
2, 5
3, 6
3, 7
It is an unlimited nested structure. Here is what I am doing (I know its wrong but want something like this):
WITH MenuItems
AS (
SELECT
CAST((ItemPath) AS VARCHAR(1000)) AS 'ItemPath',
CategoryID, Category, ChildID
FROM #Mapping
WHERE CategoryID = 1
UNION ALL
SELECT
CAST((items.ItemPath + '-/' + MenuItem.Category) AS VARCHAR(1000)) AS 'ItemPath',
MenuItem.CategoryID, MenuItem.Category, MenuItem.ChildID
FROM #Mapping AS MenuItem
JOIN MenuItems AS items
ON items.ChildID = MenuItem.CategoryID
)
select * from MenuItems
It gives me something like this:
root--------|1---|root---|2
root--------|1---|root---|3
root/Cat2---|3---|Cat2---|6
root/Cat2---|3---|Cat2---|7
root/Cat1---|2---|Cat1---|4
root/Cat1---|2---|Cat1---|5
So ideally the path should be like this:
root/parent/child (and so on)!
I'm not sure if this is what you're looking for but I've played with recursive cte's in the past and so this might be helpful in building the items path.
NOTE: I've included additional information like the Root Id and Level for each item, so that you can change the ordering of the output.
declare #Category table (Id int, Name varchar(10))
insert into #Category values (1, 'Root'),(2, 'Cat1'), (3, 'Cat2'), (4, 'Cat1.1'), (5, 'Cat1.2'), (6, 'Cat2.1'), (7, 'Cat2.2')
declare #CategoryChild table (ParentCategoryID int, ChildCategoryID int)
insert into #CategoryChild values (1, 2), (1, 3), (2, 4), (2, 5), (3, 6), (3, 7)
;with cte as
(
-- root part
select
ccParent.ChildCategoryID Id,
ccParent.ParentCategoryID ParentId,
c.Name Name,
CAST(parentCategory.Name + '/' + c.Name as varchar(1000)) as Path,
ccParent.ChildCategoryID Root,
0 as Level
from
#CategoryChild ccParent
inner join
#Category c on c.Id = ccParent.ChildCategoryID
inner join
#Category parentCategory on parentCategory.Id = ccParent.ParentCategoryID
where
ccParent.ParentCategoryID = 1
union all
-- recursive part
select
ccChild.ChildCategoryID Id,
ccChild.ParentCategoryID ParentId,
c.Name Name,
CAST((cte.Path + '/' + c.Name) as varchar(1000)) as Path,
cte.Root Root,
cte.Level + 1 as Level
from
#CategoryChild ccChild
inner join
#Category c on c.Id = ccChild.ChildCategoryID
inner join
cte on cte.Id = ccChild.ParentCategoryID
)
select cte.Path
from cte
order by cte.Root, cte.Level
Running the above within my environment gives the following results
Root/Cat1
Root/Cat1/Cat1.1
Root/Cat1/Cat1.2
Root/Cat2
Root/Cat2/Cat2.1
Root/Cat2/Cat2.2
If you were looking to include the Root category in your result set as a standalone item then you can change the first part of the cte to hard code the select of the root item.
;with cte as
(
-- root part
select
c.Id Id,
null ParentId,
c.Name Name,
CAST(c.Name as varchar(1000)) as Path,
c.Id Root,
0 as Level
from
#Category c
where
c.Name = 'Root'
union all
... same as before
Giving the follow
Root
Root/Cat1
Root/Cat1/Cat1.1
Root/Cat1/Cat1.2
Root/Cat2
Root/Cat2/Cat2.1
Root/Cat2/Cat2.2

SQL "tree-like" query - most parent group

I'm having some trouble doing a "tree-like" query (what do we call this?) in SQL.
Take a look at my diagram below (table and column names are in danish - sorry about that):
DB diagram http://img197.imageshack.us/img197/8721/44060572.jpg
Using MSSQL Server 2005, the goal is to find the most parent group (Gruppe), for each customer (Kunde).
Each group can have many parent groups and many child groups.
And, I would also like to know how to display the tree like this:
Customer 1
- Parent group 1
- Child group 1
- ChildChild group n
- Child group n
- Parent group n
- ...
- ...
Customer n
- ...
Another question:
How does the query look to get ALL the groups for all the customers? Parent and child groups.
You can use CTE's to construct "the full path" column on the fly
--DROP TABLE Gruppe, Kunde, Gruppe_Gruppe, Kunde_Gruppe
CREATE TABLE Gruppe (
Id INT PRIMARY KEY
, Name VARCHAR(100)
)
CREATE TABLE Kunde (
Id INT PRIMARY KEY
, Name VARCHAR(100)
)
CREATE TABLE Gruppe_Gruppe (
ParentGruppeId INT
, ChildGruppeId INT
)
CREATE TABLE Kunde_Gruppe (
KundeId INT
, GruppeId INT
)
INSERT Gruppe
VALUES (1, 'Group 1'), (2, 'Group 2'), (3, 'Group 3')
, (4, 'Sub-group A'), (5, 'Sub-group B'), (6, 'Sub-group C'), (7, 'Sub-group D')
INSERT Kunde
VALUES (1, 'Kunde 1'), (2, 'Kunde 2'), (3, 'Kunde 3')
INSERT Gruppe_Gruppe
VALUES (1, 4), (1, 5), (1, 7)
, (2, 6), (2, 7)
, (6, 1)
INSERT Kunde_Gruppe
VALUES (1, 1), (1, 2)
, (2, 3), (2, 4)
;WITH CTE
AS (
SELECT CONVERT(VARCHAR(1000), REPLACE(CONVERT(CHAR(5), k.Id), ' ', 'K')) AS TheKey
, k.Name AS Name
FROM Kunde k
UNION ALL
SELECT CONVERT(VARCHAR(1000), REPLACE(CONVERT(CHAR(5), x.KundeId), ' ', 'K')
+ REPLACE(CONVERT(CHAR(5), g.Id), ' ', 'G')) AS TheKey
, g.Name
FROM Gruppe g
JOIN Kunde_Gruppe x
ON g.Id = x.GruppeId
UNION ALL
SELECT CONVERT(VARCHAR(1000), p.TheKey + REPLACE(CONVERT(CHAR(5), g.Id), ' ', 'G')) AS TheKey
, g.Name
FROM Gruppe g
JOIN Gruppe_Gruppe x
ON g.Id = x.ChildGruppeId
JOIN CTE p
ON REPLACE(CONVERT(CHAR(5), x.ParentGruppeId), ' ', 'G') = RIGHT(p.TheKey, 5)
WHERE LEN(p.TheKey) < 32 * 5
)
SELECT *
, LEN(TheKey) / 5 AS Level
FROM CTE c
ORDER BY c.TheKey
Performance might be sub-optimal if you have lots of reads vs rare modifications.
I just can't say it better than Joe Celko. The problem is usually that the models built doesn't lend themselves well to build hierarchies, and that those models have to take in consideration the characteristics of your hierarchy. Is it too deep? Is it too wide? Is it narrow and shallow?
One key to success on wide and shallow trees is to have the full path in the hierarchy in a column, like Celko mentions in the first link.
http://onlamp.com/pub/a/onlamp/2004/08/05/hierarchical_sql.html
http://www.dbmsmag.com/9603d06.html and http://www.dbmsmag.com/9604d06.html
http://www.ibase.ru/devinfo/DBMSTrees/sqltrees.html
I came up with a solution that solves the problem of listing ALL the groups for each customer. Parent and child groups.
What do you think?
WITH GroupTree
AS
(
SELECT kg.KundeId, g.Id GruppeId
FROM ActiveDirectory.Gruppe g
INNER JOIN ActiveDirectory.Kunde_Gruppe kg ON g.Id = kg.GruppeId
AND (EXISTS (SELECT * FROM ActiveDirectory.Gruppe_Gruppe WHERE ParentGruppeId = g.Id)
OR NOT EXISTS (SELECT * FROM ActiveDirectory.Gruppe_Gruppe WHERE ParentGruppeId = g.Id))
UNION ALL
SELECT GroupTree.KundeId, gg.ChildGruppeId
FROM ActiveDirectory.Gruppe_Gruppe gg
INNER JOIN GroupTree ON gg.ParentGruppeId = GroupTree.GruppeId
)
SELECT KundeId, GruppeId
FROM GroupTree
OPTION (MAXRECURSION 32767)
How about something like this:
DECLARE #Customer TABLE(
CustomerID INT IDENTITY(1,1),
CustomerName VARCHAR(MAX)
)
INSERT INTO #Customer SELECT 'Customer1'
INSERT INTO #Customer SELECT 'Customer2'
INSERT INTO #Customer SELECT 'Customer3'
DECLARE #CustomerTreeStructure TABLE(
CustomerID INT,
TreeItemID INT
)
INSERT INTO #CustomerTreeStructure (CustomerID,TreeItemID) SELECT 1, 1
INSERT INTO #CustomerTreeStructure (CustomerID,TreeItemID) SELECT 2, 12
INSERT INTO #CustomerTreeStructure (CustomerID,TreeItemID) SELECT 3, 1
INSERT INTO #CustomerTreeStructure (CustomerID,TreeItemID) SELECT 3, 12
DECLARE #TreeStructure TABLE(
TreeItemID INT IDENTITY(1,1),
TreeItemName VARCHAR(MAX),
TreeParentID INT
)
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '001', NULL
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '001.001', 1
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '001.001.001', 2
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '001.001.002', 2
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '001.001.003', 2
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '001.002', 1
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '001.003', 1
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '001.003.001', 7
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '001.001.002.001', 4
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '001.001.002.002', 4
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '001.001.002.003', 4
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '002', NULL
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '002.001', 12
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '002.001.001', 13
INSERT INTO #TreeStructure (TreeItemName,TreeParentID) SELECT '002.001.002', 13
;WITH Structure AS (
SELECT TreeItemID,
TreeItemName,
TreeParentID,
REPLICATE('0',5 - LEN(CAST(TreeItemID AS VARCHAR(MAX)))) + CAST(TreeItemID AS VARCHAR(MAX)) + '\\' TreePath
FROM #TreeStructure ts
WHERE ts.TreeParentID IS NULL
UNION ALL
SELECT ts.*,
s.TreePath + REPLICATE('0',5 - LEN(CAST(ts.TreeItemID AS VARCHAR(5)))) + CAST(ts.TreeItemID AS VARCHAR(5)) + '\\' TreePath
FROM #TreeStructure ts INNER JOIN
Structure s ON ts.TreeParentID = s.TreeItemID
)
SELECT c.CustomerName,
Children.TreeItemName,
Children.TreePath
FROM #Customer c INNER JOIN
#CustomerTreeStructure cts ON c.CustomerID = cts.CustomerID INNER JOIN
Structure s ON cts.TreeItemID = s.TreeItemID INNER JOIN
(
SELECT *
FROM Structure
) Children ON Children.TreePath LIKE s.TreePath +'%'
ORDER BY 1,3
OPTION (MAXRECURSION 0)
In T-SQL, you can write a while loop. Untested:
#group = <starting group>
WHILE (EXISTS(SELECT * FROM Gruppe_Gruppe WHERE ChildGruppeId=#group))
BEGIN
SELECT #group=ParentGruppeId FROM Gruppe_Gruppe WHERE ChildGruppeId=#group
END
We use SQL Server 2000 and there is an example of expanding hierarchies using a stack in the SQL Books Online, I have written a number of variants for our ERP system
http://support.microsoft.com/kb/248915
I gather that there is a Native method using CTE within SQL 2005 but I have not used it myself