The following statement uses 2 separate recursive CTE statements to build a list of steps from a starting location to an ending location based on trips. The desired output is correct, however I am wondering if it is possible to consolidate the 2 CTE statements into one.
The difficulty I am having is relating the endLocation to the startLocation in the first recursive iteration cte1.
The database is SQL Server 2017. I have added the SQL fiddle below:
[SQL Fiddle][1]
SQL Server 2017 Schema Setup:
Create Table TripLocation
(TripID int,
LocationID int,
StopOrder int
)
Create table FromTo
(tripID int,
fromLocationID int,
fromStopOrder int,
toLocationID int,
toStopOrder int
)
Create table cte1Temp
(startTripID int,
startLocationID int,
tripID int,
fromLocationID int,
toLocationID int,
step int)
Create table cte2Temp
(startTripID int,
startLocationID int,
endLocationID int,
tripID int,
fromLocationID int,
toLocationID int,
step int)
--LIST OF LOCATIONS FOR EACH TRIP
Insert into TripLocation
Values
(1,1,0),
(1,2,1),
(1,1,2),
(2,2,0),
(2,3,1),
(2,2,2),
(3,3,0),
(3,4,1),
(3,3,2)
--LIST OF POSSIBLE TO/FROM COMBINATIONS FOR EACH TRIP BASED ON STOPORDER
insert into FromTo
select
FromLocation.tripID,
FromLocation.LocationID [fromLocationID],
FromLocation.StopOrder [fromStopOrder],
ToLocation.LocationID [toLocationID],
ToLocation.StopOrder [toStopOrder]
from
TripLocation FromLocation
join TripLocation ToLocation
on FromLocation.tripID = ToLocation.tripID
and ToLocation.StopOrder >= FromLocation.StopOrder
and FromLocation.LocationID <> ToLocation.LocationID
;
--FIND ALL POSSIBLE END LOCATIONS FOR EACH START LOCATION IF TRIPS SHARE A COMMON LOCATION
with cte1 as
(
select
tripID [startTripID],
fromLocationID [startLocationID],
tripID,
fromLocationID,
toLocationID,
1 [step]
from
FromTo
union all
select
anchor.startTripID,
anchor.startLocationID,
member.tripID,
member.fromLocationID,
member.toLocationID,
anchor.step + 1 [step]
from
FromTo member
join cte1 anchor
on anchor.toLocationID = member.fromLocationID
and member.toLocationID <> anchor.fromLocationID
and member.tripID <> anchor.tripID
)
insert into cte1Temp
select
*
from
cte1
;
--GENERATE PLAN FOR EACH START LOCATION TO AN END LOCATION
with cte2 as
(
select
startTripID,
StartLocationID,
ToLocationID [EndLocationID],
tripID,
FromLocationID,
ToLocationID,
step
from
cte1Temp
union all
select
b.startTripID,
b.StartLocationID,
b.ToLocationID,
a.tripID,
a.FromLocationID,
a.ToLocationID,
a.step
from
cte1Temp b
join CTE2 a
on a.endLocationID = b.FromLocationID
and a.startLocationID = b.startLocationID
)
insert into cte2Temp
select
*
from
cte2
Query 1:
select
*
from
cte2Temp
order by
startlocationID, endLocationID, step
Results:
| startTripID | startLocationID | endLocationID | tripID | fromLocationID | toLocationID | step |
|-------------|-----------------|---------------|--------|----------------|--------------|------|
| 1 | 1 | 2 | 1 | 1 | 2 | 1 |
| 1 | 1 | 3 | 1 | 1 | 2 | 1 |
| 1 | 1 | 3 | 2 | 2 | 3 | 2 |
| 1 | 1 | 4 | 1 | 1 | 2 | 1 |
| 1 | 1 | 4 | 2 | 2 | 3 | 2 |
| 1 | 1 | 4 | 3 | 3 | 4 | 3 |
| 1 | 2 | 1 | 1 | 2 | 1 | 1 |
| 2 | 2 | 3 | 2 | 2 | 3 | 1 |
| 2 | 2 | 4 | 2 | 2 | 3 | 1 |
| 2 | 2 | 4 | 3 | 3 | 4 | 2 |
| 2 | 3 | 1 | 2 | 3 | 2 | 1 |
| 2 | 3 | 1 | 1 | 2 | 1 | 2 |
| 2 | 3 | 2 | 2 | 3 | 2 | 1 |
| 3 | 3 | 4 | 3 | 3 | 4 | 1 |
| 3 | 4 | 1 | 3 | 4 | 3 | 1 |
| 3 | 4 | 1 | 2 | 3 | 2 | 2 |
| 3 | 4 | 1 | 1 | 2 | 1 | 3 |
| 3 | 4 | 2 | 3 | 4 | 3 | 1 |
| 3 | 4 | 2 | 2 | 3 | 2 | 2 |
| 3 | 4 | 3 | 3 | 4 | 3 | 1 |
You can try this
Create Table TripLocation
(TripID int,
LocationID int,
StopOrder int
)
Create table FromTo
(tripID int,
fromLocationID int,
fromStopOrder int,
toLocationID int,
toStopOrder int
)
Create table cte1Temp
(startTripID int,
startLocationID int,
tripID int,
fromLocationID int,
toLocationID int,
step int,
path varchar(max)
)
--LIST OF LOCATIONS FOR EACH TRIP
Insert into TripLocation
Values
(1,1,0),
(1,2,1),
(1,1,2),
(2,2,0),
(2,3,1),
(2,2,2),
(3,3,0),
(3,4,1),
(3,3,2)
--LIST OF POSSIBLE TO/FROM COMBINATIONS FOR EACH TRIP BASED ON STOPORDER
insert into FromTo
select
FromLocation.tripID,
FromLocation.LocationID [fromLocationID],
FromLocation.StopOrder [fromStopOrder],
ToLocation.LocationID [toLocationID],
ToLocation.StopOrder [toStopOrder]
from
TripLocation FromLocation
join TripLocation ToLocation
on FromLocation.tripID = ToLocation.tripID
and ToLocation.StopOrder >= FromLocation.StopOrder
and FromLocation.LocationID <> ToLocation.LocationID
;
--FIND ALL POSSIBLE END LOCATIONS FOR EACH START LOCATION IF TRIPS SHARE A COMMON LOCATION
with cte1 as
(
select
tripID [startTripID],
fromLocationID [startLocationID],
tripID,
fromLocationID,
toLocationID,
1 [step],
cast('1_' + ltrim(str(tripID)) + '-' + ltrim(str(tolocationID)) as varchar(max)) [path]
from
FromTo
union all
select
anchor.startTripID,
anchor.startLocationID,
member.tripID,
member.fromLocationID,
member.toLocationID,
anchor.step + 1 [step],
anchor.path + ',' + ltrim(str(anchor.step + 1)) + '_' + + ltrim(str(member.tripID)) + '-' + ltrim(str(member.toLocationID))
from
FromTo member
join cte1 anchor
on anchor.toLocationID = member.fromLocationID
and member.toLocationID <> anchor.fromLocationID
and member.tripID <> anchor.tripID
)
insert into cte1Temp
select
*
from
cte1
;
select
StartLocationID,
TolocationId,
Substring(Value, 1,Charindex('-', Value)-1) as Trip,
Substring(Value, Charindex('-', Value)+1, LEN(Value)) as CrossingLocationID
from
cte1Temp
CROSS APPLY STRING_SPLIT(path, ',')
order by StartLocationId, ToLocationId, Trip
Results:
StartLocationID TolocationId Trip CrossingLocationID
1 2 1_1 2
1 3 1_1 2
1 3 2_2 3
1 4 1_1 2
1 4 2_2 3
1 4 3_3 4
2 1 1_1 1
2 3 1_2 3
2 4 1_2 3
2 4 2_3 4
3 1 1_2 2
3 1 2_1 1
3 2 1_2 2
3 4 1_3 4
4 1 1_3 3
4 1 2_2 2
4 1 3_1 1
4 2 1_3 3
4 2 2_2 2
4 3 1_3 3
Related
I have a Microsoft SQL Server with the following tables:
Projects
BookedHours (with fk_Project = Projects.ID)
Products
ProjectsToProducts (n:m with fk_Projects = Projects.ID and fk_Products = Products.ID)
I now want to select how many hours are booked to which product per month. The problem is, that one project can have multiple products (that's why I need the n:m table).
If I do the following, it will count the hours twice if a project has two products.
SELECT
P.ID AS fk_Product, MONTH(B.Datum) AS Monat, SUM(B.Hours) AS Stunden
FROM
tbl_BookedHours AS B
INNER JOIN
tbl_Projects AS M on B.fk_Project = M.ID
INNER JOIN
tbl_ProjectProduct AS PP ON PP.fk_Project = M.ID
INNER JOIN
tbl_Products AS P ON PP.fk_Product = P.ID
WHERE
YEAR(B.Datum) = 2020
GROUP BY
P.ID, MONTH(B.Datum)
ORDER BY
P.ID, MONTH(B.Datum)
I can get the number of products for each project with this SQL:
SELECT fk_Project, COUNT(*) AS Cnt
FROM tbl_ProjectProduct
GROUP By fk_MainProject
But how can I now divide the hours for each project by its individual factor and add it all up per product and month?
I could do it in my C# program or I could use a cursor and iterate through all projects, but I think there should be an more elegant way.
Edit with sample data:
|----------------| |----------------| |------------------------------|
| tbl_Projects | | tbl_Products | | tbl_ProjectProduct |
|----------------| |----------------| |------------------------------|
| ID | Name | | ID | Name | | ID | fk_Project | fk_Product |
|----+-----------| |----+-----------| |------------------------------|
| 1 | Project 1 | | 1 | Product 1 | | 1 | 1 | 1 |
| 2 | Project 2 | | 2 | Product 2 | | 2 | 1 | 2 |
| 3 | Project 3 | | 3 | Product 3 | | 3 | 2 | 1 |
| 4 | Project 4 | | 4 | Product 4 | | 4 | 3 | 3 |
|----------------| |----------------| | 5 | 4 | 1 |
| 6 | 4 | 2 |
| 7 | 4 | 4 |
|------------------------------|
|--------------------------------------|
| tbl_BookedHours |
|--------------------------------------|
| ID | fk_Project | Hours | Date |
|--------------------------------------|
| 1 | 1 | 10 | 2020-01-15 |
| 2 | 1 | 20 | 2020-01-20 |
| 3 | 2 | 10 | 2020-01-15 |
| 4 | 3 | 30 | 2020-01-18 |
| 5 | 2 | 20 | 2020-01-20 |
| 6 | 4 | 30 | 2020-01-25 |
| 7 | 1 | 10 | 2020-02-15 |
| 8 | 1 | 20 | 2020-02-20 |
| 9 | 2 | 10 | 2020-02-15 |
| 10 | 3 | 30 | 2020-03-18 |
| 11 | 2 | 20 | 2020-03-20 |
| 12 | 4 | 30 | 2020-03-25 |
|--------------------------------------|
The Result should be:
|----------------------------|
| fk_Product | Month | Hours |
|----------------------------|
| 1 | 1 | 55 |
| 2 | 1 | 25 |
| 3 | 1 | 30 |
| 4 | 1 | 10 |
| 1 | 2 | 25 |
| 2 | 2 | 15 |
| 1 | 3 | 30 |
| 2 | 3 | 10 |
| 3 | 3 | 30 |
| 4 | 3 | 10 |
|----------------------------|
For example booking Nr. 1 has to be divided by 2 (because Project 1 has two products) and one half of amount added to Product 1 and the other to Product 2 (Both in January). Booking Nr. 4 should not be divided, because Project 3 only has one product. Booking Numer 12 for example has to be divided by 3.
So that in total the Hours in the end add up to the same total.
I hope it's clearer now.
*** EDIT 2***
DECLARE #tbl_Projects TABLE (ID INT, [Name] VARCHAR(MAX))
INSERT INTO #tbl_Projects VALUES
(1,'Project 1'),
(2,'Project 2'),
(3,'Project 3'),
(4,'Project 4')
DECLARE #tbl_Products TABLE (ID INT, [Name] VARCHAR(MAX))
INSERT INTO #tbl_Products VALUES
(1,'Product 1'),
(2,'Product 2'),
(3,'Product 3'),
(4,'Product 4')
DECLARE #tbl_ProjectProduct TABLE (ID INT, fk_Project int, fk_Product int)
INSERT INTO #tbl_ProjectProduct VALUES
(1,1,1),
(2,1,2),
(3,2,1),
(4,3,3),
(5,4,1),
(6,4,2),
(7,4,4)
DECLARE #tbl_BookedHours TABLE (ID INT, fk_Project int, Hours int, [Date] Date)
INSERT INTO #tbl_BookedHours VALUES
(1,1,10,'2020-01-15'),
(2,1,20,'2020-01-20'),
(3,2,10,'2020-01-15'),
(4,3,30,'2020-01-18'),
(5,2,20,'2020-01-20'),
(6,4,30,'2020-01-25'),
(7,1,10,'2020-02-15'),
(8,1,20,'2020-02-20'),
(9,2,10,'2020-02-15'),
(10,3,30,'2020-03-18'),
(11,2,20,'2020-03-20'),
(12,4,30,'2020-03-25')
SELECT P.ID AS fk_Product, MONTH(B.Date) AS Month, SUM(B.Hours) AS SumHours
FROM #tbl_BookedHours AS B INNER JOIN #tbl_Projects AS M on B.fk_Project = M.ID
INNER JOIN #tbl_ProjectProduct AS PP ON PP.fk_Project = M.ID
INNER JOIN #tbl_Products AS P ON PP.fk_Product = P.ID
GROUP BY P.ID,MONTH(B.Date)
ORDER BY P.ID, MONTH(B.Date)
This gives me the wrong result, because it Counts the hours for both products:
| fk_Product | Month | SumHours |
|-------------------------------|
| 1 | 1 | 90 |
| 1 | 2 | 40 |
| 1 | 3 | 50 |
| 2 | 1 | 60 |
| 2 | 2 | 30 |
| 2 | 3 | 30 |
| 3 | 1 | 30 |
| 3 | 3 | 30 |
| 4 | 1 | 30 |
| 4 | 3 | 30 |
|-------------------------------|
Consider the following query. I modified your table variables to temp tables so it was easier to debug.
;WITH CTE AS
(
SELECT fk_Project, count(fk_Product) CNT
FROM #tbl_ProjectProduct
GROUP BY fk_Project
)
,CTE2 AS
(
SELECT t1.Date, t2.fk_Project, Hours/CNT NewHours
FROM #tbl_BookedHours t1
INNER JOIN CTE t2 on t1.fk_Project = t2.fk_Project
)
SELECT t4.ID fk_Product, MONTH(date) MN, SUM(NewHours) HRS
FROM CTE2 t1
INNER JOIN #tbl_Projects t2 on t1.fk_Project = t2.id
INNER JOIN #tbl_ProjectProduct t3 on t3.fk_Project = t2.ID
INNER JOIN #tbl_Products t4 on t4.ID = t3.fk_Product
GROUP BY t4.ID,MONTH(date)
I have client ids and their dates of login. i want to calculate the week number with respect to their first login date
i am fairly new to sql
Demo output
ClientID Date of login Week Number
1 2019-12-20 1
1 2019-12-21 1
1 2019-12-21 1
1 2019-12-22 1
1 2019-12-29 2
1 2019-12-29 2
2 2020-01-27 1
2 2020-01-28 1
2 2020-02-05 2
2 2020-02-06 2
2 2020-02-16 3
This is very trivial date arithmetic that just requires the min DateOfLogin for each ClientID, which you can find with a windowed function.
Calculate the datediff in days between this date and the current DateOfLogin, integer divide by 7 (to return no fractional days) and then add 1 to correctly offset the WeekNum value:
declare #l table(ClientID int, DateOfLogin date);
insert into #l values(1,'2019-12-20'),(1,'2019-12-21'),(1,'2019-12-21'),(1,'2019-12-22'),(1,'2019-12-29'),(1,'2019-12-29'),(2,'2020-01-27'),(2,'2020-01-28'),(2,'2020-02-05'),(2,'2020-02-06'),(2,'2020-02-16');
select ClientID
,DateOfLogin
,(datediff(day,min(DateOfLogin) over (partition by ClientID),DateOfLogin) / 7) + 1 as WeekNum
from #l;
Output
+----------+-------------+---------+
| ClientID | DateOfLogin | WeekNum |
+----------+-------------+---------+
| 1 | 2019-12-20 | 1 |
| 1 | 2019-12-21 | 1 |
| 1 | 2019-12-21 | 1 |
| 1 | 2019-12-22 | 1 |
| 1 | 2019-12-29 | 2 |
| 1 | 2019-12-29 | 2 |
| 2 | 2020-01-27 | 1 |
| 2 | 2020-01-28 | 1 |
| 2 | 2020-02-05 | 2 |
| 2 | 2020-02-06 | 2 |
| 2 | 2020-02-16 | 3 |
+----------+-------------+---------+
This query returns the week number.
select DATENAME(WW, '2019-12-20')
This is for MSSQL.
Here might be a solution for you, you'll maybe just have to look at the way you are going to do the insert and maybe optimize it a bit better.
select 1 AS 'ClientID', '2019-12-20' AS 'LogInDate', 1 AS 'Week'
into #test
insert into #test
select top(1) 1, '2020-02-05', case DATEDIFF(week,'2020-02-05',LogInDate) when 0 then week else Week +1 end from #test where ClientID = 1 order by LogInDate desc
I have different tables through I made temp table and here is the result set of temp table:
car_id | car_type | status | count
--------+----------+---------+------
100421 | 1 | 1 | 9
100421 | 1 | 2 | 8
100421 | 1 | 3 | 3
100421 | 2 | 1 | 6
100421 | 2 | 2 | 8
100421 | 2 | 3 | 3
100422 | 1 | 1 | 5
100422 | 1 | 2 | 8
100422 | 1 | 3 | 7
Here is the meaning of status column:
1 as sale
2 as purchase
3 as return
Now I want to show this result set as below
car_id | car_type | sale | purchase | return
--------+----------+------+----------+----------
100421 | 1 | 9 | 8 | 3
100421 | 2 | 6 | 8 | 3
100422 | 1 | 5 | 8 | 7
I tried but unable to generate this result set. Can anyone help?
You can also use a CASE expression.
Query
select [car_id], [car_type],
max(case [status] when 1 then [count] end) as [sale],
max(case [status] when 2 then [count] end) as [purchase],
max(case [status] when 3 then [count] end) as [return]
from [your_table_name]
group by [car_id], [car_type]
order by [car_id];
Try this
select car_id ,car_type, [1] as Sale,[2] as Purchase,[3] as [return]
from (select car_id , car_type , [status] ,[count] from tempTable)d
pivot(sum([count]) for [status] in([1],[2],[3]) ) as pvt
also you can remove the subquery if you don't have any condition
like
select car_id ,car_type, [1] as Sale,[2] as Purchase,[3] as [return]
from tempTable d
pivot(sum([count]) for [status] in([1],[2],[3]) ) as pvt
Table that I am using is CADIGR, and column DIGR_KEY1 is always a parent number (DIGR_KEY of the parent) in the row.
I must update CADIGR DIGR_KEY1 (parent number), but first I must check that I am not updating child with one of theirs parent.
I really don't have idea how to do this? Child can have a lot of parents, so check must be done on the whole tree up to the root (where DIGR_KEY1 IS NULL).
Only if the number does not exist as parent number of that child's tree, update is possible.
CREATE TABLE CADIGR
(
DIGR_KEY INT,
DIGR_KEY1 INT
)
INSERT INTO CADIGR (DIGR_KEY, DIGR_KEY1)
VALUES
(1, NULL),
(2,1),
(3,1),
(4,1),
(11,4),
(12,4),
(13,4),
(5,2),
(6,2),
(5,2),
(6,2),
(7,5),
(8,5),
(9,5),
(10,5),
(14,3),
(15,3)
You can use a recursive query like this, can use level to perform operations on each group of childs.
WITH Rec as
(
SELECT digr_key, digr_key1, 1 AS level
FROM CADIGR
WHERE digr_key1 is null
UNION ALL
SELECT c.digr_key, c.digr_key1, level = level + 1
FROM CADIGR c
INNER JOIN rec
ON c.digr_key1 = rec.digr_key
)
SELECT *
FROM Rec;
GO
digr_key | digr_key1 | level
-------: | --------: | ----:
1 | null | 1
2 | 1 | 2
3 | 1 | 2
4 | 1 | 2
11 | 4 | 3
12 | 4 | 3
13 | 4 | 3
14 | 3 | 3
15 | 3 | 3
5 | 2 | 3
6 | 2 | 3
5 | 2 | 3
6 | 2 | 3
7 | 5 | 4
8 | 5 | 4
9 | 5 | 4
10 | 5 | 4
7 | 5 | 4
8 | 5 | 4
9 | 5 | 4
10 | 5 | 4
dbfiddle here
Avoiding duplicated records
WITH DistData as
(
SELECT DISTINCT digr_key, digr_key1
FROM CADIGR
)
, Rec as
(
SELECT digr_key, digr_key1, 1 AS level
FROM DistData
WHERE digr_key1 is null
UNION ALL
SELECT c.digr_key, c.digr_key1, level = level + 1
FROM DistData c
INNER JOIN rec
ON c.digr_key1 = rec.digr_key
)
SELECT *
FROM Rec;
GO
digr_key | digr_key1 | level
-------: | --------: | ----:
1 | null | 1
2 | 1 | 2
3 | 1 | 2
4 | 1 | 2
11 | 4 | 3
12 | 4 | 3
13 | 4 | 3
14 | 3 | 3
15 | 3 | 3
5 | 2 | 3
6 | 2 | 3
7 | 5 | 4
8 | 5 | 4
9 | 5 | 4
10 | 5 | 4
dbfiddle here
I have an exported table from accounting software like below.
AccountID AccountName
--------- -----------
11 Acc11
12 Acc12
13 Acc13
11/11 Acc11/11
11/12 Acc11/12
11/111 Acc11/111
11/11/001 Acc11/11/001
11/11/002 Acc11/11/002
12/111 Acc12/111
12/112 Acc12/112
I want to convert it to tree query in MS-SQL Server 2008 to use it as a Treelist datasource in my win aaplication.
I raised this question before and it's answered with a way that it was very very slow for my big table with more than 5000 records (Create Tree Query From Numeric Mapping Table in SQL). But I think counting "/" and separating AccountID field with "/" can solve my problem easier and very faster.
Anyway, My expected result must be like below:
AccountID AccountName ID ParentID Level HasChild
--------- ----------- --- --------- ------ --------
11 Acc11 1 Null 1 1
12 Acc12 2 Null 1 1
13 Acc13 3 Null 1 0
11/11 Acc11/11 4 1 2 1
11/12 Acc11/12 5 1 2 0
11/111 Acc11/111 6 1 2 0
11/11/001 Acc11/11/001 7 4 3 0
11/11/002 Acc11/11/002 8 4 3 0
12/111 Acc12/111 9 2 2 0
12/112 Acc12/112 10 2 2 0
Please Help Me.
I modified my answer given in the first question...
It would be best, if your table would keep the relation data directly in indexed columns. Before you change your table's structure you might try this:
A table with test data
DECLARE #tbl TABLE ( AccountID VARCHAR(100), AccountName VARCHAR(100));
INSERT INTO #tbl VALUES
('11','Acc11')
,('12','Acc12')
,('13','Acc13')
,('11/11','Acc11/11')
,('11/12','Acc11/12')
,('11/111','Acc11/111')
,('11/11/001','Acc11/11/001')
,('11/11/002','Acc11/11/002')
,('12/111','Acc12/111')
,('12/112','Acc12/112');
This will get the needed data into a newly created temp table called #tempHierarchy
SELECT AccountID
,AccountName
,ROW_NUMBER() OVER(ORDER BY LEN(AccountID)-LEN(REPLACE(AccountID,'/','')),AccountID) AS ID
,Extended.HierarchyLevel
,STUFF(
(
SELECT '/' + A.B.value('.','varchar(10)')
FROM Extended.IDsXML.nodes('/x[position() <= sql:column("HierarchyLevel")]') AS A(B)
FOR XML PATH('')
),1,2,'') AS ParentPath
,Extended.IDsXML.value('/x[sql:column("HierarchyLevel")+1][1]','varchar(10)') AS ownID
,Extended.IDsXML.value('/x[sql:column("HierarchyLevel")][1]','varchar(10)') AS ancestorID
INTO #tempHierarchy
FROM #tbl
CROSS APPLY(SELECT LEN(AccountID)-LEN(REPLACE(AccountID,'/','')) + 1 AS HierarchyLevel
,CAST('<x></x><x>' + REPLACE(AccountID,'/','</x><x>') + '</x>' AS XML) AS IDsXML) AS Extended
;
The intermediate result
+-----------+--------------+----+----------------+------------+-------+------------+
| AccountID | AccountName | ID | HierarchyLevel | ParentPath | ownID | ancestorID |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11 | Acc11 | 1 | 1 | | 11 | |
+-----------+--------------+----+----------------+------------+-------+------------+
| 12 | Acc12 | 2 | 1 | | 12 | |
+-----------+--------------+----+----------------+------------+-------+------------+
| 13 | Acc13 | 3 | 1 | | 13 | |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/11 | Acc11/11 | 4 | 2 | 11 | 11 | 11 |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/111 | Acc11/111 | 5 | 2 | 11 | 111 | 11 |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/12 | Acc11/12 | 6 | 2 | 11 | 12 | 11 |
+-----------+--------------+----+----------------+------------+-------+------------+
| 12/111 | Acc12/111 | 7 | 2 | 12 | 111 | 12 |
+-----------+--------------+----+----------------+------------+-------+------------+
| 12/112 | Acc12/112 | 8 | 2 | 12 | 112 | 12 |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/11/001 | Acc11/11/001 | 9 | 3 | 11/11 | 001 | 11 |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/11/002 | Acc11/11/002 | 10 | 3 | 11/11 | 002 | 11 |
+-----------+--------------+----+----------------+------------+-------+------------+
And now a similar recursive approach takes place as in my first answer. But - as it is using a real table now and all the string splitting has taken place already - it should be faster...
WITH RecursiveCTE AS
(
SELECT th.*
,CAST(NULL AS BIGINT) AS ParentID
,CASE WHEN EXISTS(SELECT 1 FROM #tempHierarchy AS x WHERE x.ParentPath=th.AccountID) THEN 1 ELSE 0 END AS HasChild
FROM #tempHierarchy AS th WHERE th.HierarchyLevel=1
UNION ALL
SELECT sa.AccountID
,sa.AccountName
,sa.ID
,sa.HierarchyLevel
,sa.ParentPath
,sa.ownID
,sa.ancestorID
,(SELECT x.ID FROM #tempHierarchy AS x WHERE x.AccountID=sa.ParentPath)
,CASE WHEN EXISTS(SELECT 1 FROM #tempHierarchy AS x WHERE x.ParentPath=sa.AccountID) THEN 1 ELSE 0 END AS HasChild
FROM RecursiveCTE AS r
INNER JOIN #tempHierarchy AS sa ON sa.HierarchyLevel=r.HierarchyLevel+1
AND r.AccountID=sa.ParentPath
)
SELECT r.AccountID
,r.AccountName
,r.ID
,r.ParentID
,r.HierarchyLevel
,r.HasChild
FROM RecursiveCTE AS r
ORDER BY HierarchyLevel,ParentID;
And finally I clean up
DROP TABLE #tempHierarchy;
And here's the final result
+-----------+--------------+----+----------+----------------+----------+
| AccountID | AccountName | ID | ParentID | HierarchyLevel | HasChild |
+-----------+--------------+----+----------+----------------+----------+
| 11 | Acc11 | 1 | NULL | 1 | 1 |
+-----------+--------------+----+----------+----------------+----------+
| 12 | Acc12 | 2 | NULL | 1 | 1 |
+-----------+--------------+----+----------+----------------+----------+
| 13 | Acc13 | 3 | NULL | 1 | 0 |
+-----------+--------------+----+----------+----------------+----------+
| 11/11 | Acc11/11 | 4 | 1 | 2 | 1 |
+-----------+--------------+----+----------+----------------+----------+
| 11/111 | Acc11/111 | 5 | 1 | 2 | 0 |
+-----------+--------------+----+----------+----------------+----------+
| 11/12 | Acc11/12 | 6 | 1 | 2 | 0 |
+-----------+--------------+----+----------+----------------+----------+
| 12/111 | Acc12/111 | 7 | 2 | 2 | 0 |
+-----------+--------------+----+----------+----------------+----------+
| 12/112 | Acc12/112 | 8 | 2 | 2 | 0 |
+-----------+--------------+----+----------+----------------+----------+
| 11/11/001 | Acc11/11/001 | 9 | 4 | 3 | 0 |
+-----------+--------------+----+----------+----------------+----------+
| 11/11/002 | Acc11/11/002 | 10 | 4 | 3 | 0 |
+-----------+--------------+----+----------+----------------+----------+