How to write a SQL statement to return all children of each root,extend to each root - sql

I have a table.
the value of [contents] can insert any word(Below are just examples)
create table #TreeTable (
Id uniqueidentifier,
Contents NVARCHAR(200),
ParentId uniqueidentifier,
Floors Int,
Sort Int,
)
insert into #TreeTable([Id], [ParentId], [Contents],[Floors],[Sort]) values
(1, 0, '1',1,1),
(2, 0, '2',1,2),
(3, 1, '1-1',2,1),
(4, 1, '1-2',2,2),
(5, 2, '2-1',2,1),
(6, 3, '1-1-1',3,1),
(7, 4, '1-2-1',3,1);
(8, 4, '1-2-2',3,2),
(9, 6, '1-1-1-1',4,1),
(10, 6,'1-1-1-2',4,2);
I want to write a T-SQL statement that will return all children of each root with the root ID and Below is the result I expected
[Id][ParentId][Contents] [Levels][Sort]
(1, 0, '1', 1, 1)
(3, 1, '1-1', 2, 1)
(6, 3, '1-1-1', 3, 1)
(9, 6, '1-1-1-1', 4, 1)
(10, 6, '1-1-1-2', 4, 2)
(4, 1, '1-2', 2, 2)
(7, 4, '1-2-1', 3, 1)
(8, 4, '1-2-2', 3, 2)
(2, 0, '2', 1, 2)
(5, 2, '2-1', 2, 1)
The depth of the tree may be arbitrarily long
I try to use this ,but the reuslt is not what i expected
with RecursiveTable as(
------ start-------
SELECT a.*
FROM TreeTable a
WHERE
a.ParentId = 0
------ end ---------
union all
SELECT b.*
FROM TreeTable b
join RecursiveTable a on a.Id =b.ParentId
)
select * from RecursiveTable

If we may believe [Contents] values, then simply
SELECT [Id],
[ParentId],
[Contents],
LEN([Contents]) - LEN(REPLACE([Contents], '-', '')) + 1 [Levels],
[Sort]
FROM #TreeTable
ORDER BY [Contents];
fiddle
If not then
WITH cte AS
(
SELECT [Id], [ParentId], CAST([Sort] AS NCHAR), [Sort], 1 [Levels]
FROM #TreeTable
WHERE [ParentId] = 0
UNION ALL
SELECT t.[Id], t.[ParentId], cte.[Contents] + '-' + t.[Sort], t.[Sort], 1 + cte.[Levels]
FROM #TreeTable t
JOIN cte ON t.[ParentId] = cte.[Id]
)
SELECT [Id], [ParentId], [Contents], [Levels], [Sort]
FROM cte
ORDER BY [Contents];
fiddle (optimize types adjusting for their matching in CTE by yourself).

U can try:
WITH RECURSIVETABLE (ID, [PARENTID], [CONTENTS],[LEVELS],[SORT],[ORDER]) AS
(
------ START-------
SELECT A.ID, A.[PARENTID], A.[CONTENTS],A.[FLOORS],A.[SORT], CONVERT(VARCHAR(250),RIGHT('00000'+CONVERT(VARCHAR(50),ROW_NUMBER() OVER(ORDER BY SORT)),5))
FROM #TREETABLE A
WHERE
A.PARENTID = 0
------ END ---------
UNION ALL
SELECT B.ID, B.[PARENTID], B.[CONTENTS],B.[FLOORS],B.[SORT], CONVERT(VARCHAR(250),CONVERT(VARCHAR,A.[ORDER])+'.'+RIGHT('00000'+CONVERT(VARCHAR(50),ROW_NUMBER() OVER(ORDER BY B.[FLOORS],B.SORT)),5))
FROM #TREETABLE B
JOIN RECURSIVETABLE A ON A.ID =B.PARENTID
)
SELECT * FROM RECURSIVETABLE ORDER BY [ORDER]

Related

TSQL - Select Latest Date by 2 Type

I need select data in SQL Server.
I have a Data example like this
DECLARE #tblA TABLE (ID int, SubId int, Createdate DateTime)
INSERT INTO #tblA VALUES (1, 1, '10/21/2020')
, (2, 1, '10/21/2020')
, (3, 1, '10/27/2020')
, (4, 2, '10/21/2020')
, (5, 2, '10/21/2020')
, (6, 1, '10/21/2020')
, (7, 2, '10/23/2020')
, (8, 2, '10/22/2020')
, (9, 1, '10/25/2020')
, (10, 3, '10/21/2020')
I want to get 4 records (in table will have a lot of SubId)
2 records latest date by SubId = 1
2 records latest date by SubId = 2
Following example, I expect Select with like table Output
DECLARE #tblOutput Table (ID int, SubId int, Createdate DateTime)
INSERT INTO #tblOutput VALUES (3, 1, '10/27/2020')
, (9, 1, '10/25/2020')
, (7, 2, '10/23/2020')
, (8, 2, '10/22/2020')
"tooltip"
I try with Union but It's only Order after Union. It's not the result I want.
Please, Help me to select this.
Thank you.
You can use the Row_Number() function as shown below:
Create table tblA (ID int, SubId int, Createdate DateTime)
Insert Into tblA values (1, 1, '10/21/2020')
, (2, 1, '10/21/2020')
, (3, 1, '10/27/2020')
, (4, 2, '10/21/2020')
, (5, 2, '10/21/2020')
, (6, 1, '10/21/2020')
, (7, 2, '10/23/2020')
, (8, 2, '10/22/2020')
, (9, 1, '10/25/2020')
, (10, 3, '10/21/2020')
select Id
, SubId
, Createdate
from(
select *
, Row_Number() Over (Partition By SubId order by Createdate desc) as RNo
from tblA
)temp where RNo <= 2 and SubId in (1, 2)
Order by SubId
Live db<>fiddle demo

SQL select distinct data without ordering the column

I have table which contains data as below:
DECLARE #CheckList TABLE
(
RowNumber INT IDENTITY(1,1) INT,
CheckId INT,
Treat INT
)
INSERT INTO #CheckList
VALUES (1, 1, 1), (1, 3, 1), (1, 2, 1), (1, 1, 1),
(1, 3, 1), (1, 2, 1), (1, 1, 3), (1, 3, 3),
(1, 2, 3);
SELECT DISTINCT Id, CheckId
FROM #CheckList
WHERE Id = 1
Where I want to select the data from this table as
Id CheckId
------------
1 1
1 3
1 2
I want the same order of the check id which is available in table. Can you help me with this?
Retain original ordering by RowNumber identity column
select Id,CheckId
from(
select distinct Id,CheckId, min(rownumber) over(partition by Id,CheckId) rn
from #CheckList
where Id=1
) t
order by rn;
Db fiddle
you can check this
DECLARE #CheckList TABLE
( Id INT , CheckId INT, Treat INT )
INSERT INTO #CheckList
VALUES (1, 1, 1), (1, 3, 1), (1, 2, 1), (1, 1, 1),(1, 3, 1), (1, 2, 1), (1, 1, 3),(1,3,3), (1, 2, 3);
SELECT DISTINCT Id, CheckId
FROM #CheckList
WHERE Id = 1

Identifying/comparing sets of rows within groups

I have a matter which seemed simple to solve but now I find it troublesome.
In simplification - I need to find a way to identify unique sets of rows within groups defined by another column. In basic example the source table contains only two columns:
routeID nodeID nodeName
1 1 a
1 2 b
2 1 a
2 2 b
3 1 a
3 2 b
4 1 a
4 2 c
5 1 a
5 2 c
6 1 a
6 2 b
6 3 d
7 1 a
7 2 b
7 3 d
So, the routeID column refers to set of nodes which define a route.
What I need to do is to somehow group the routes, so that there will be only one unique sequence of nodes for one routeID.
In my actual case I tried to use window function to add columns which help to identify nodes sequence, but I still have no idea how to get those unique sequences and group routes.
As a final effect I want to get only unique routes - for example routes 1,2 and 3 aggregated to one route.
Do you have any idea how to help me ?
EDIT:
The other table which I would like to join with the one from the example may look like that:
journeyID nodeID nodeName routeID
1 1 a 1
1 2 b 1
2 1 a 1
2 2 b 1
3 1 a 4
3 2 c 4
...........................
...........................
You can try this idea:
DECLARE #DataSource TABLE
(
[routeID] TINYINT
,[nodeID] TINYINT
,[nodeName] CHAR(1)
);
INSERT INTO #DataSource ([routeID], [nodeID], [nodeName])
VALUES ('1', '1', 'a')
,('1', '2', 'b')
,('2', '1', 'a')
,('2', '2', 'b')
,('3', '1', 'a')
,('3', '2', 'b')
,('4', '1', 'a')
,('4', '2', 'c')
,('5', '1', 'a')
,('5', '2', 'c')
,('6', '1', 'a')
,('6', '2', 'b')
,('6', '3', 'd')
,('7', '1', 'a')
,('7', '2', 'b')
,('7', '3', 'd');
SELECT DS.[routeID]
,nodes.[value]
,ROW_NUMBER() OVER (PARTITION BY nodes.[value] ORDER BY [routeID]) AS [rowID]
FROM
(
-- getting unique route ids
SELECT DISTINCT [routeID]
FROM #DataSource DS
) DS ([routeID])
CROSS APPLY
(
-- for each route id creating CSV list with its node ids
SELECT STUFF
(
(
SELECT ',' + [nodeName]
FROM #DataSource DSI
WHERE DSI.[routeID] = DS.[routeID]
ORDER BY [nodeID]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
) nodes ([value]);
The code will give you this output:
So, you simple need to filter by rowID = 1. Of course, you can change the code as you like in order to satisfy your bussness criteria (for example showing no the first route ID with same nodes, but the last).
Also, ROW_NUMBER function cannot be used directly in the WHERE clause, so you need to wrap the code before filtering:
WITH DataSource AS
(
SELECT DS.[routeID]
,nodes.[value]
,ROW_NUMBER() OVER (PARTITION BY nodes.[value] ORDER BY [routeID]) AS [rowID]
FROM
(
-- getting unique route ids
SELECT DISTINCT [routeID]
FROM #DataSource DS
) DS ([routeID])
CROSS APPLY
(
-- for each route id creating CSV list with its node ids
SELECT STUFF
(
(
SELECT ',' + [nodeName]
FROM #DataSource DSI
WHERE DSI.[routeID] = DS.[routeID]
ORDER BY [nodeID]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
) nodes ([value])
)
SELECT DS2.*
FROM DataSource DS1
INNER JOIN #DataSource DS2
ON DS1.[routeID] = DS2.[routeID]
WHERE DS1.[rowID] = 1;
ok, let's use some recursion to create a complete node list for each routeID
First of all let's populate source table and journeyes tale
-- your source
declare #r as table (routeID int, nodeID int, nodeName char(1))
-- your other table
declare #j as table (journeyID int, nodeID int, nodeName char(1), routeID int)
-- temp results table
declare #routes as table (routeID int primary key, nodeNames varchar(1000))
;with
s as (
select *
from (
values
(1, 1, 'a'),
(1, 2, 'b'),
(2, 1, 'a'),
(2, 2, 'b'),
(3, 1, 'a'),
(3, 2, 'b'),
(4, 1, 'a'),
(4, 2, 'c'),
(5, 1, 'a'),
(5, 2, 'c'),
(6, 1, 'a'),
(6, 2, 'b'),
(6, 3, 'd'),
(7, 1, 'a'),
(7, 2, 'b'),
(7, 3, 'd')
) s (routeID, nodeID, nodeName)
)
insert into #r
select *
from s
;with
s as (
select *
from (
values
(1, 1, 'a', 1),
(1, 2, 'b', 1),
(2, 1, 'a', 1),
(2, 2, 'b', 1),
(3, 1, 'a', 4),
(3, 2, 'c', 4)
) s (journeyID, routeID, nodeID, nodeName)
)
insert into #j
select *
from s
now let's exctract routes:
;with
d as (
select *, row_number() over (partition by r.routeID order by r.nodeID desc) n2
from #r r
),
r as (
select d.*, cast(nodeName as varchar(1000)) Names, cast(0 as bigint) i2
from d
where nodeId=1
union all
select d.*, cast(r.names + ',' + d.nodeName as varchar(1000)), r.n2
from d
join r on r.routeID = d.routeID and r.nodeId=d.nodeId-1
)
insert into #routes
select routeID, Names
from r
where n2=1
table #routes will be like this:
routeID nodeNames
1 'a,b'
2 'a,b'
3 'a,b'
4 'a,c'
5 'a,c'
6 'a,b,d'
7 'a,b,d'
an now the final output:
-- the unique routes
select MIN(r.routeID) routeID, nodeNames
from #routes r
group by nodeNames
-- the unique journyes
select MIN(journeyID) journeyID, r.nodeNames
from #j j
inner join #routes r on j.routeID = r.routeID
group by nodeNames
output:
routeID nodeNames
1 'a,b'
4 'a,c'
6 'a,b,d'
and
journeyID nodeNames
1 'a,b'
3 'a,c'

MINIMUM on second column, take first and third

DECLARE #Foo TABLE (Id INT, PozId INT, Val INT)
INSERT #Foo (Id, PozId, Val)
VALUES
(1, 1, 34),
(1, 2, 976),
(2, 1, 235),
(2, 2, 792),
(3, 2, 456),
(3, 3, 123)
How to get results like this from above query?
(1, 1, 34)
(2, 1, 235)
(3, 2, 456)
This brings you desired result. Query partitions your Ids and picks lowest PozitionId.
DECLARE #Foo TABLE
(
Id INT, PozId INT, Val INT
);
INSERT #Foo
(Id, PozId, Val)
VALUES
(1, 1, 34)
, (1, 2, 976)
, (2, 1, 235)
, (2, 2, 792)
, (3, 2, 456)
, (3, 3, 123);
SELECT Id, PozId, Val
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY Id ORDER BY PozId) AS RowNo, *
FROM #Foo
) AS T
WHERE RowNo = 1;

Concatenating in one column based on another column

I'm working with SQL Server 2008.
Let's say I have these three tables:
Product
1, "Honda Civic"
2, "Fiat Ritmo"
3, "Ford Fiesta"
ProductGroup
1, 1, 1
2, 1, 2
3, 3, 1
4, 2, 3
5, 2, 4
6, 1, 5
Group
1 American
2 Asian
3 European
4 Old
5 Fast
And let's say I want to query these tables so as to get the following:
"Fiat Ritmo", "European, Old"
"Honda Civic", "Asian, Fast"
"Ford Focus", "American"
How can I do this?
I'm using paging on the client side so I can't group the pages there.
Thanks for your help,
Barry
Something like this:
First some test data:
DECLARE #Product TABLE(ID INT, Name VARCHAR(100))
INSERT INTO #Product
SELECT 1,'Honda Civic' UNION ALL
SELECT 2,'Fiat Ritmo' UNION ALL
SELECT 3,'Ford Fiesta'
DECLARE #ProductGroup TABLE(ID INT,ProductID INT, GroupID INT)
INSERT INTO #ProductGroup
SELECT 1, 1, 1 UNION ALL
SELECT 2, 1, 2 UNION ALL
SELECT 3, 3, 1 UNION ALL
SELECT 4, 2, 3 UNION ALL
SELECT 5, 2, 4 UNION ALL
SELECT 6, 1, 5
DECLARE #Group TABLE(ID INT, Name VARCHAR(100))
INSERT INTO #Group
SELECT 1, 'American' UNION ALL
SELECT 2, 'Asian' UNION ALL
SELECT 3, 'European' UNION ALL
SELECT 4, 'Old' UNION ALL
SELECT 5, 'Fast'
Then the query:
SELECT
Product.ID,
Product.Name,
STUFF
(
(
SELECT
',' + Groups.Name
FROM
#ProductGroup AS ProductGroup
JOIN #Group AS Groups
ON ProductGroup.GroupID=Groups.ID
WHERE
ProductGroup.ProductID = Product.ID
FOR XML PATH('')
),1,1,'') AS Names
FROM
#Product AS Product
Use FOR XML PATH feature to get string grouping like this:
DECLARE #Product TABLE (Id INT PRIMARY KEY, Name NVARCHAR(100) NOT NULL)
INSERT #Product
VALUES(1, 'Honda Civic'), (2, 'Fiat Ritmo'), (3, 'Ford Fiesta')
DECLARE #ProductGroup TABLE(Product INT, Grp INT, PRIMARY KEY(Product, Grp))
INSERT #ProductGroup
VALUES
(1, 1),
(1, 2),
(3, 1),
(2, 3),
(2, 4),
(1, 5)
DECLARE #Grp TABLE(Id INT PRIMARY KEY, NAME NVARCHAR(100))
INSERT #Grp
VALUES
(1, 'American'),
(2, 'Asian'),
(3, 'European'),
(4, 'Old'),
(5, 'Fast')
SELECT p.name,
(
SELECT Name+','
FROM #ProductGroup pg
JOIN #Grp g
ON pg.Grp = g.Id
WHERE pg.Product = p.Id
FOR XML PATH('')
) Groups
FROM #Product p