Related
I have been looking into ways to do this in a single UPDATE statement but have not been successful.
This is a sample of what the dataset I am working with looks like:
+-------------------------+----------+--------------+----+--------+
| TIMESTAMP | USERNAME | VALUE | ID | IsDupe |
+-------------------------+----------+--------------+----+--------+
| 2020-02-12 07:00:03.000 | LINA | ORDER1 | 1 | 0 |
| 2020-02-12 07:00:03.000 | LINA | ITEM1 | 2 | 0 |
| 2020-02-12 07:09:09.000 | LINA | FINISH BUILD | 3 | 0 |
| 2020-02-12 07:09:10.000 | LINA | ORDER1 | 4 | 0 |
| 2020-02-12 07:09:11.000 | LINA | ITEM2 | 5 | 0 |
| 2020-02-12 07:24:07.000 | LINA | FINISH BUILD | 6 | 0 |
| 2020-02-12 07:24:08.000 | NAGA | ORDER2 | 7 | 0 |
| 2020-02-12 07:24:10.000 | NAGA | ITEM3 | 8 | 0 |
| 2020-02-12 07:45:06.000 | NAGA | FINISH BUILD | 9 | 0 |
| 2020-02-12 07:45:12.000 | NAGA | FINISH BUILD | 10 | 1 |
| 2020-02-12 07:45:13.000 | XELLOS | ORDER3 | 11 | 0 |
| 2020-02-12 07:45:14.000 | XELLOS | ITEM4 | 12 | 0 |
| 2020-02-12 07:56:36.000 | XELLOS | FINISH BUILD | 13 | 0 |
| 2020-02-12 07:56:39.000 | GOURRY | ORDER4 | 14 | 0 |
| 2020-02-12 07:56:40.000 | GOURRY | ITEM5 | 15 | 0 |
| 2020-02-12 08:30:11.000 | GOURRY | FINISH BUILD | 17 | 0 |
+-------------------------+----------+--------------+----+--------+
What I want to do is create an additional column that works as an iterator, breaking each of these rows into sets of three, like so:
+-------------------------+----------+--------------+-------+--------+-------+
| TIMESTAMP | USERNAME | VALUE | IDCol | IsDupe | SetID |
+-------------------------+----------+--------------+-------+--------+-------+
| 2020-02-12 07:00:03.000 | LINA | ORDER1 | 1 | 0 | 1 |
| 2020-02-12 07:00:03.000 | LINA | ITEM1 | 2 | 0 | 1 |
| 2020-02-12 07:09:09.000 | LINA | FINISH BUILD | 3 | 0 | 1 |
| 2020-02-12 07:09:10.000 | LINA | ORDER1 | 4 | 0 | 2 |
| 2020-02-12 07:09:11.000 | LINA | ITEM2 | 5 | 0 | 2 |
| 2020-02-12 07:24:07.000 | LINA | FINISH BUILD | 6 | 0 | 2 |
| 2020-02-12 07:24:08.000 | NAGA | ORDER2 | 7 | 0 | 3 |
| 2020-02-12 07:24:10.000 | NAGA | ITEM3 | 8 | 0 | 3 |
| 2020-02-12 07:45:06.000 | NAGA | FINISH BUILD | 9 | 0 | 3 |
| 2020-02-12 07:45:12.000 | NAGA | FINISH BUILD | 10 | 1 | NULL |
| 2020-02-12 07:45:13.000 | XELLOS | ORDER3 | 11 | 0 | 4 |
| 2020-02-12 07:45:14.000 | XELLOS | ITEM4 | 12 | 0 | 4 |
| 2020-02-12 07:56:36.000 | XELLOS | FINISH BUILD | 13 | 0 | 4 |
| 2020-02-12 07:56:39.000 | GOURRY | ORDER4 | 14 | 0 | 5 |
| 2020-02-12 07:56:40.000 | GOURRY | ITEM5 | 15 | 0 | 5 |
| 2020-02-12 08:30:11.000 | GOURRY | FINISH BUILD | 17 | 0 | 5 |
+-------------------------+----------+--------------+-------+--------+-------+
I have tried looking up iterative statements in SQL but there have been huge concerns about performance as this is going to be a relatively large set of data and this statement would need to be run during the day, impacting production.
Also note that there are potentially duplicates or other errors contained within the dataset. Rows where IsDupe is set to 1 must be ignored from this statement.
I've been trying to build a cursor to do this, but have been running into numerous problems with syntax as well as just general inexperience writing cursors:
DECLARE #MyCursor CURSOR;
DECLARE #SetID INT;
DECLARE #OUTPUTNUM TINYINT;
DECLARE #COUNTER TINYINT;
BEGIN
SET #MyCursor = CURSOR LOCAL FAST_FORWARD FOR
SELECT IsDupe from dbo.MyDataTable
WHERE IsDupe != 1
OPEN #MyCursor
FETCH NEXT FROM #MyCursor INTO #SetID
WHILE ##FETCH_STATUS = 0 BEGIN
SET #COUNTER = 0;
SET #OUTPUTNUM = 1;
WHILE #COUNTER < 3
BEGIN
UPDATE dbo.MyDataTable SET dbo.MyDataTable.SetID = #OUTPUTNUM
SET #COUNTER = #COUNTER + 1
END
SET #COUNTER = 0;
SET #OUTPUTNUM = #OUTPUTNUM + 1
FETCH NEXT FROM #MyCursor
INTO #SetID
END;
CLOSE #MyCursor ;
DEALLOCATE #MyCursor;
END;
When I run this, I get the following message:
[2:07:54 PM] Started executing query at Line 1
Commands completed successfully.
Total execution time: 00:00:00.026
But no results, the SetID column values are still all null.
You can do it without cursor using windows functions:
select [TIMESTAMP], USERNAME, VALUE, ID, IsDupe,
case
when IsDupe = 1 then null
else DENSE_RANK()over(order by GroupID)
end as SetID
from(
select
*,
case when value like 'ORDER%' then ID
when value like 'ITEM%' then lag(ID,1)over (order by ID)
when value like 'FINISH BUILD%' then lag(ID,2)over (order by ID)
end as GroupID
from #tmp where IsDupe = 0
)a
union
select
[TIMESTAMP], USERNAME, VALUE, ID, IsDupe, null as SetID
from #tmp where IsDupe = 1
order by ID
Here is my full example:
drop table #tmp
select '2020-02-12 07:00:03.000' as TIMESTAMP, 'LINA' as USERNAME , 'ORDER1' as VALUE , 1 as ID , 0 as IsDupe into #tmp
union select '2020-02-12 07:00:03.000' , 'LINA' , 'ITEM1' , 2 , 0
union select '2020-02-12 07:09:09.000' , 'LINA' , 'FINISH BUILD' , 3 , 0
union select '2020-02-12 07:09:10.000' , 'LINA' , 'ORDER1' , 4 , 0
union select '2020-02-12 07:09:11.000' , 'LINA' , 'ITEM2' , 5 , 0
union select '2020-02-12 07:24:07.000' , 'LINA' , 'FINISH BUILD' , 6 , 0
union select '2020-02-12 07:24:08.000' , 'NAGA' , 'ORDER2' , 7 , 0
union select '2020-02-12 07:24:10.000' , 'NAGA' , 'ITEM3' , 8 , 0
union select '2020-02-12 07:45:06.000' , 'NAGA' , 'FINISH BUILD' , 9 , 0
union select '2020-02-12 07:45:12.000' , 'NAGA' , 'FINISH BUILD' , 10 , 1
union select '2020-02-12 07:45:13.000' , 'XELLOS' , 'ORDER3' , 11 , 0
union select '2020-02-12 07:45:14.000' , 'XELLOS' , 'ITEM4' , 12 , 0
union select '2020-02-12 07:56:36.000' , 'XELLOS' , 'FINISH BUILD' , 13 , 0
union select '2020-02-12 07:56:39.000' , 'GOURRY' , 'ORDER4' , 14 , 0
union select '2020-02-12 07:56:40.000' , 'GOURRY' , 'ITEM5' , 15 , 0
union select '2020-02-12 08:30:11.000' , 'GOURRY' , 'FINISH BUILD' , 17 , 0
order by ID
select [TIMESTAMP], USERNAME, VALUE, ID, IsDupe,
case
when IsDupe = 1 then null
else DENSE_RANK()over(order by GroupID)
end as SetID
from(
select
*,
case when value like 'ORDER%' then ID
when value like 'ITEM%' then lag(ID,1)over (order by ID)
when value like 'FINISH BUILD%' then lag(ID,2)over (order by ID)
end as GroupID
from #tmp where IsDupe = 0
)a
union
select
[TIMESTAMP], USERNAME, VALUE, ID, IsDupe, null as SetID
from #tmp where IsDupe = 1
order by ID
Output:
TIMESTAMP USERNAME VALUE IDCol IsDupe SetID
2020-02-12 07:00:03.000 LINA ORDER1 1 0 1
2020-02-12 07:00:03.000 LINA ITEM1 2 0 1
2020-02-12 07:09:09.000 LINA FINISH BUILD 3 0 1
2020-02-12 07:09:10.000 LINA ORDER1 4 0 2
2020-02-12 07:09:11.000 LINA ITEM2 5 0 2
2020-02-12 07:24:07.000 LINA FINISH BUILD 6 0 2
2020-02-12 07:24:08.000 NAGA ORDER2 7 0 3
2020-02-12 07:24:10.000 NAGA ITEM3 8 0 3
2020-02-12 07:45:06.000 NAGA FINISH BUILD 9 0 3
2020-02-12 07:45:12.000 NAGA FINISH BUILD 10 1 NULL
2020-02-12 07:45:13.000 XELLOS ORDER3 11 0 4
2020-02-12 07:45:14.000 XELLOS ITEM4 12 0 4
2020-02-12 07:56:36.000 XELLOS FINISH BUILD 13 0 4
2020-02-12 07:56:39.000 GOURRY ORDER4 14 0 5
2020-02-12 07:56:40.000 GOURRY ITEM5 15 0 5
2020-02-12 08:30:11.000 GOURRY FINISH BUILD 17 0 5
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
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
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 |
+-----------+--------------+----+----------+----------------+----------+
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
can you tell me
how to dived
ID SN Types
1 123 ABC,XYZ,TEST, RJK,CDF,TTT,UMB,UVX
2 234 RJK,CDF,TTT,UMB,UVX,TTT,UMB,UVX
3 345 OID,XYZ,TTT,UMB,UVX,TTT,UMB,UVX
as
ID SN Types1 Types2 Types3 Types4 Types5 Types6 Types7 Types8
1 123 ABC XYZ TEST RJK CDF TTT UMB UVX
2 234 RJK CDF TTT UMB UVX TTT UMB UVX
3 345 OID XYZ TTT UMB UVX TTT UMB UVX
please send me sql code
thanx,
First, create a split function that maintains order:
CREATE FUNCTION [dbo].[SplitStrings_Ordered]
(
#List NVARCHAR(MAX),
#Delimiter NVARCHAR(255)
)
RETURNS TABLE
AS
RETURN (SELECT [Index] = ROW_NUMBER() OVER (ORDER BY Number), Item
FROM (SELECT Number, Item = SUBSTRING(#List, Number,
CHARINDEX(#Delimiter, #List + #Delimiter, Number) - Number)
FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects) AS n(Number)
WHERE Number <= CONVERT(INT, LEN(#List))
AND SUBSTRING(#Delimiter + #List, Number, LEN(#Delimiter)) = #Delimiter
) AS y);
Now, you can use this function in combination with a PIVOT. This is a self-enclosed example but you can just replace #d in the last query with whatever your real table is.
DECLARE #d TABLE(ID INT, SN INT, Types NVARCHAR(MAX));
INSERT #d VALUES
(1,123,N'ABC,XYZ,TEST, RJK,CDF,TTT,UMB,UVX'),
(2,234,N'RJK,CDF,TTT,UMB,UVX,TTT,UMB,UVX' ),
(3,345,N'OID,XYZ,TTT,UMB,UVX,TTT,UMB,UVX' );
SELECT ID,SN,
Types1 = [1], Types2 = [2], Types3 = [3], Types4 = [4],
Types1 = [5], Types6 = [6], Types7 = [7], Types8 = [8]
FROM #d AS d CROSS APPLY dbo.SplitStrings_Ordered(d.Types, ',') AS y
PIVOT (MAX(Item) FOR [Index] IN ([1],[2],[3],[4],[5],[6],[7],[8])) AS p;
T-SQL is not well suited for dealing with comma-separated values. However with creative use of a Recursive CTE, it is possible to split up the columns without too much effort. The following query makes the assumption that there is never more than 8 types total in a row in the original data - it can be trivially extended to handle any number of types (albeit not dynamic, the maximum number must be known):
;with TypesSplit as (
select [ID], [SN], 1 as [Index],
case when CHARINDEX(',', [Types]) <> 0 then substring([Types], CHARINDEX(',', [Types])+1, 1000) else '' end as [Types],
case when CHARINDEX(',', [Types]) <> 0 then substring([Types], 1, CHARINDEX(',', [Types])-1) else [Types] end as [Type]
from Types
union all
select [ID], [SN], [Index] + 1 as [Index],
case when CHARINDEX(',', [Types]) <> 0 then substring([Types], CHARINDEX(',', [Types])+1, 1000) else '' end as [Types],
case when CHARINDEX(',', [Types]) <> 0 then substring([Types], 1, CHARINDEX(',', [Types])-1) else [Types] end as [Type]
from TypesSplit
where LEN([Types])>0
)
select [ID],[SN],
max(case when [Index]=1 then [Type] end) as Types1,
max(case when [Index]=2 then [Type] end) as Types2,
max(case when [Index]=3 then [Type] end) as Types3,
max(case when [Index]=4 then [Type] end) as Types4,
max(case when [Index]=5 then [Type] end) as Types5,
max(case when [Index]=6 then [Type] end) as Types6,
max(case when [Index]=7 then [Type] end) as Types7,
max(case when [Index]=8 then [Type] end) as Types8
from TypesSplit
group by [ID],[SN]
(Demo: http://www.sqlfiddle.com/#!6/c522f/9)
The basic idea is to use CHARINDEX to find the locations of each comma, then SUBSTRING to split out each type from the beginning. The recursion loops through all of the types until there are none left to consume. The types are numbered via the [Index] column to allow reproducing them in the same order in the output. The output is basically a pivot table of the row data generated by the recursive CTE, which looks like this before pivoting:
| ID | SN | INDEX | TYPES | TYPE |
|----|-----|-------|------------------------------|------|
| 1 | 123 | 1 | XYZ,TEST,RJK,CDF,TTT,UMB,UVX | ABC |
| 2 | 234 | 1 | CDF,TTT,UMB,UVX,TTT,UMB,UVX | RJK |
| 3 | 345 | 1 | XYZ,TTT,UMB,UVX,TTT,UMB,UVX | OID |
| 3 | 345 | 2 | TTT,UMB,UVX,TTT,UMB,UVX | XYZ |
| 3 | 345 | 3 | UMB,UVX,TTT,UMB,UVX | TTT |
| 3 | 345 | 4 | UVX,TTT,UMB,UVX | UMB |
| 3 | 345 | 5 | TTT,UMB,UVX | UVX |
| 3 | 345 | 6 | UMB,UVX | TTT |
| 3 | 345 | 7 | UVX | UMB |
| 3 | 345 | 8 | | UVX |
| 2 | 234 | 2 | TTT,UMB,UVX,TTT,UMB,UVX | CDF |
| 2 | 234 | 3 | UMB,UVX,TTT,UMB,UVX | TTT |
| 2 | 234 | 4 | UVX,TTT,UMB,UVX | UMB |
| 2 | 234 | 5 | TTT,UMB,UVX | UVX |
| 2 | 234 | 6 | UMB,UVX | TTT |
| 2 | 234 | 7 | UVX | UMB |
| 2 | 234 | 8 | | UVX |
| 1 | 123 | 2 | TEST,RJK,CDF,TTT,UMB,UVX | XYZ |
| 1 | 123 | 3 | RJK,CDF,TTT,UMB,UVX | TEST |
| 1 | 123 | 4 | CDF,TTT,UMB,UVX | RJK |
| 1 | 123 | 5 | TTT,UMB,UVX | CDF |
| 1 | 123 | 6 | UMB,UVX | TTT |
| 1 | 123 | 7 | UVX | UMB |
| 1 | 123 | 8 | | UVX |
After pivoting, the final output looks like this:
| ID | SN | TYPES1 | TYPES2 | TYPES3 | TYPES4 | TYPES5 | TYPES6 | TYPES7 | TYPES8 |
|----|-----|--------|--------|--------|--------|--------|--------|--------|--------|
| 1 | 123 | ABC | XYZ | TEST | RJK | CDF | TTT | UMB | UVX |
| 2 | 234 | RJK | CDF | TTT | UMB | UVX | TTT | UMB | UVX |
| 3 | 345 | OID | XYZ | TTT | UMB | UVX | TTT | UMB | UVX |