Reverse order of a CTE result? - sql-server

Here's my code, to traverse all nodes starting from one:
SELECT * INTO MyTable
FROM
(
SELECT 1 Id, 1 ParentId, 'Parent A' Name
UNION ALL
SELECT 5,1,'Child A1'
UNION ALL
SELECT 47894,5,'Child A2'
UNION ALL
SELECT 2,2, 'Parent B'
UNION ALL
SELECT 3,2, 'Child B1'
)TAB
;With CTE as
(
select * from MyTable where Id = 47894
union all
select a.* from MyTable a inner join cte b
on a.Id=b.ParentId and a.Id<>b.Id
)
select STRING_AGG(Name, ' >> ') from CTE
With input 47894, it gives:
Child A2 >> Child A1 >> Parent A
What if I need the reverse?
Parent A >> Child A1 >> Child A2

You can use this:
SELECT *
INTO MyTable
FROM (
SELECT
1 Id,
1 ParentId,
'Parent A' Name
UNION ALL
SELECT
5,
1,
'Child A1'
UNION ALL
SELECT
47894,
5,
'Child A2'
UNION ALL
SELECT
2,
2,
'Parent B'
UNION ALL
SELECT
3,
2,
'Child B1'
) TAB;
WITH CTE
AS (SELECT
Id,
ParentId,
Name,
CAST(1 AS INT) AS OrderByValue
FROM MyTable
WHERE Id = 47894
UNION ALL
SELECT
a.Id,
a.ParentId,
a.Name,
OrderByValue + 1
FROM MyTable a
INNER JOIN CTE b ON a.Id = b.ParentId
AND a.Id <> b.Id)
SELECT STRING_AGG(Name, ' >> ')WITHIN GROUP(ORDER BY CTE.OrderByValue DESC)
FROM CTE;

Related

How to Join Array of struct on table1 with normal column of table 2 in BigQuery

Tabel1 and Table 2
select p.id,q.description
from table1 p
join table2 q
on q.control_number
in unnest(p.results.control_number)
When I process this I'm getting the below error:
Cannot access field control_number on a value with type
ARRAY<STRUCT<control_number string,...., …>>
I have also tried un-nesting after the table like:
select p.id,q.f.description
from table1 p,Unnest(finder) f
join table2 q
on q.control_number in unnest(p.f.results.control_number)
But this also did not work.
Can anyone tell me what's wrong?
Another option
select p.id,q.description
from table1 p, table2 q
where q.control_number in (select control_number from p.finder)
Try the following in standard SQL:
with table1 as (
select '1' id, array[struct('a1' as control_number)] finder
UNION ALL
select '2' id, array[struct('a2' as control_number)] finder
UNION ALL
select '3' id, array[struct('a3' as control_number)] finder
),
table2 as (
select 'description for a1' description, 'a1' control_number
UNION ALL
select 'description for a2' description, 'a2' control_number
)
select p.id,q.description
from table1 p, unnest(p.finder) f join table2 q on q.control_number = f.control_number;
WITH clause is used to simulate data in your tables.

SQL - Comma Delimited subset results of multiple column in its own column

I have a view with a union all table, a person may belong to one or many table.
how do i create a query that will add a column with a ';' delimited where the person belong to, the ID is unique per person.
here's the example
--table1
PID fName tableMem
1 test group1
2 test2 group1
--table2
PID fName tableMem
1 test group2
3 test3 group2
--table3
PID fName tableMem
1 test group3
3 test3 group3
Here's the output I wanted
--compiled table after union of all the 3 tables
PID fname tableMem
1 test group1;group2;group3
2 test2 group1
3 test3 group2;group3
Here's the query I built from reading here for the past 2 days.I'm using STUFF and partition because I need the row to be distinct and this query will run as view.
SELECT *
FROM
(SELECT
*,
ROW_NUMBER() OVER(PARTITION BY PIP ORDER BY Fname) AS rownum
FROM
(SELECT
*,
STUFF((SELECT ';'+ di.tablemem
FROM DPI di <<-- alias table from union
WHERE DPI.PID = di.PID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') tablemem
FROM
(SELECT *
FROM
(--table1
SELECT 'group1' AS tableMem, * FROM table1
UNION ALL
--table2
SELECT 'group2' AS tableMem, * FROM table2
UNION ALL
--table3
SELECT 'group3' AS tableMem, * FROM table3) AS DPI <<--alias table name
) AS innertable
) AS distinctTable
) AS outerTable
WHERE
rownum = 1
what am I missing or what is wrong with the query. I'm guessing its because Im using a derived table name of the union sub select. is there any workaround?
Thank you in advance
You need GROUP BY data by PID, fName, using the FOR XML aggregation it would be
WITH DPI AS (
--table1
Select 'group1' as tableMem,* from table1
UNION ALL
--table2
Select 'group2' as tableMem,* from table2
UNION ALL
--table3
Select 'group3' as tableMem,* from table3
)
SELECT PID, fName
, STUFF((
SELECT ';'+ di.tablemem
FROM DPI di
WHERE di.PID = di1.PID
FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'),1,1,'') tablemem
FROM DPI di1
GROUP BY PID, fName;
Here is a possible solution using CTE:
;with cte as (
select * from #tbl1
union all
select * from #tbl2
union all
select * from #tbl3
)
select distinct
pid
,fname
,stuff(
(select '; ' + tableMem
from cte
where pid = a.pid
and fname = a.fname
FOR XML PATH(''),TYPE)
.value('text()[1]','nvarchar(max)'),1,2,N'') as tableMem
from cte a
Given that you have a finite number of tables, I think I find this simpler:
select p.pid, p.fname,
trim(';' from
(case when t1.pid is not null then 'group1;' else '' end) +
(case when t2.pid is not null then 'group2;' else '' end) +
(case when t3.pid is not null then 'group3;' else '' end)
) as groups
from (select pid, fname from table1 union -- on purpose to remove duplicates
select pid, fname from table2 union
select pid, fname from table3
) p left join
table1 t1
on t1.pid = p.pid left join
table2 t2
on t2.pid = p.pid left join
table3 t3
on t3.pid = p.pid;
The most recent version of SQL Server supports string_agg(), which would make this simpler still.
Try this:
DECLARE #table1 TABLE
(
[PID] TINYINT
,[fname] VARCHAR(12)
,[tableMem] VARCHAR(12)
);
DECLARE #table2 TABLE
(
[PID] TINYINT
,[fname] VARCHAR(12)
,[tableMem] VARCHAR(12)
);
DECLARE #table3 TABLE
(
[PID] TINYINT
,[fname] VARCHAR(12)
,[tableMem] VARCHAR(12)
);
INSERT INTO #table1 ([PID], [fname], [tableMem])
VALUES (1, 'test', 'group1')
,(2, 'test2', 'group1');
INSERT INTO #table2 ([PID], [fname], [tableMem])
VALUES (1, 'test', 'group2')
,(3, 'test3 ', 'group2');
INSERT INTO #table3 ([PID], [fname], [tableMem])
VALUES (1, 'test', 'group3')
,(3, 'test3 ', 'group3');
WITH DataSource AS
(
SELECT *
FROM #table1
UNION ALL
SELECT *
FROM #table2
UNION ALL
SELECT *
FROM #table3
)
SELECT DISTINCT DS.[PID]
,DS.fname
,CSVvalue.[tableMem]
FROM DataSource DS
CROSS APPLY
(
SELECT STUFF
(
(
SELECT ',' + DS1.[tableMem]
FROM DataSource DS1
WHERE DS.[PID] = DS1.[PID]
AND DS.[fname] = DS1.[fname]
ORDER BY DS1.[tableMem]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
) CSVvalue ([tableMem]);

How to set an order when using CTE?

I have a table with 2 columns (id, childId). The data is as follows:
1, 2
3, 4
2, null
4, null
I'm using a CTE so that I get the child records:
DECLARE #id TABLE (id int);
INSERT INTO #id SELECT 1;
INSERT INTO #id SELECT 3;
WITH cte AS
(
SELECT id, childId
FROM mytable
WHERE
id IN (SELECT id FROM #id)
UNION ALL
SELECT b.id, b.childId
FROM mytable b
INNER JOIN cte
ON b.id = cte.childId
)
SELECT * FROM cte
The result always come back as:
1, 2
3, 4
4, null
2, null
But I need the result to look like:
1, 2
2, null
3, 4,
4, null
That is, first the anchor records then the records for the recursive sql for each anchor record.
Is this possible?
Add a static value to in anchor query. Then in recursive part add a static value greater than the static value of anchor query. Now the use static value in Order by
Try this
WITH cte AS
(
SELECT 0 as rn, id, childId
FROM mytable
WHERE
id IN (SELECT id FROM #id)
UNION ALL
SELECT 1 as rn,b.id, b.childId
FROM mytable b
INNER JOIN cte
ON b.id = cte.childId
)
SELECT * FROM cte
Order by rn,id
Also consider adding option(Maxrecursion N). By default it just makes only 100 recursions
By Adding a Seq, the results will be displayed in the proper order/nesting
DECLARE #id TABLE (id int);
INSERT INTO #id SELECT 1;
INSERT INTO #id SELECT 3;
WITH cte AS
(
SELECT id, childId
,Seq = cast(100000+Row_Number() over (Order by id) as varchar(500))
FROM mytable
WHERE
id IN (SELECT id FROM #id)
UNION ALL
SELECT b.id, b.childId
,Seq = cast(concat(cte.Seq,'.',100000+Row_Number() over (Order by b.id)) as varchar(500))
FROM mytable b
INNER JOIN cte
ON b.id = cte.childId
)
SELECT * FROM cte
Order By Seq

How to select two table parent and child hierarchy using joins in sql?

I am try to select parent child hierarchy from two different tables, but am not getting correct output..if any one know tell me..
Table1
Id title
1 a1
2 b
3 c1
4 d1
Table2
Id title pid
1 a null
2 b 1
3 c 2
4 d 1
Check if the table1 id equal to table2 pid then get table1 title.
Output Like
Id title
1 a1
2 a1<b
3 a1<b<c1
4 a1<d1
SELECT T2.PId AS MId, CASE WHEN T2.Id IS NOT NULL THEN T2.Title + '>' + T1. title ELSE T1. title END AS title, T2.Id AS PId
FROM(SELECT T0.Id AS Id, CASE WHEN T1.Id IS NOT NULL THEN T1.Title + '>' + T0. title ELSE T0.title END AS title, T1.PId AS PId
FROM (SELECT T1.PgeId AS MnuId, T1.Title AS title, adnMNU.PId AS PId
FROM TABLE1 T1 join TABLE2 ON T1.Id = TABLE2.Id ) T0
Left JOIN adnPGE T2 ON T0.PId=T1.Id )T1
Left JOIN adnPGE T3 ON T1.PId=T2.Id
I'd use a recursive common table expression (CTE). One note, it doesn't look like you're using the titles from Table2. Just an observation; it doesn't hurt my feelings any. As such, I created a view of the data that uses the Ids from Table2 and the titles from Table1.
with Table1 as (
select * from (values
(1, 'a1'),
(2, 'b '),
(3, 'c1'),
(4, 'd1')
) as x(Id, title)
), Table2 as (
select * from (values
(1, 'a', null),
(2, 'b', 1 ),
(3, 'c', 2 ),
(4, 'd', 1 )
) as x(Id, title, pid)
), combined as (
select b.Id, a.title, b.pid
from Table1 as a
join Table2 as b
on a.Id = b.Id
), r_cte as (
select *, cast(title as varchar(max)) as [path]
from combined
where pid is null
union all
select child.*, cast(concat(parent.[path], '>', child.title) as varchar(max))
from r_cte as parent
join combined as child
on child.pid = parent.Id
)
select *
from r_cte
order by id;

SQL query for displaying count if same name comes in adjacent row it should show the count else 1

I have a table tb1 with columns id,name,
if same name comes in adjacent row it should display the count count else 1
For eg:
id name
1 sam
2 jose
3 sam
4 sam
5 dev
6 jose
Result want to be
name counts
sam 1
jose 1
sam 2
dev 1
jose 1
please help.
Check out this one :(SELF JOIN)
create table #sampele(id int,name varchar(50))
insert into #sampele values(1,'sam')
insert into #sampele values(2,'jose')
insert into #sampele values(3,'sam')
insert into #sampele values(4,'sam')
insert into #sampele values(5,'dev')
insert into #sampele values(6,'jose')
select a.id,a.name,case when a.name = b.name then 2 else 1 end as cnt from
#sampele a
left outer join
#sampele b
on a.id = b.id+1
Try a combination with a sub query, "COUNT(*) OVER (PARTITION", and row_number():
--DROP TABLE #Test;
SELECT id = IDENTITY(INT,1,1), name INTO #Test FROM
(
SELECT name = 'sam' UNION ALL
SELECT 'jose' UNION ALL
SELECT 'sam ' UNION ALL
SELECT 'sam ' UNION ALL
SELECT 'sam ' UNION ALL
SELECT 'dev ' UNION ALL
SELECT 'dev ' UNION ALL
SELECT 'jose' UNION ALL
SELECT 'sam ' UNION ALL
SELECT 'sam ' UNION ALL
SELECT 'jose'
) a;
GO
WITH GetEndID AS (
SELECT *
, EndID =(SELECT MIN(id) FROM #Test b WHERE b.name != a.name AND b.id > a.id)
FROM #Test a
), GetCount AS
(
SELECT
*
, NameCount = COUNT(*) OVER (PARTITION BY EndID)
, OrderPrio = ROW_NUMBER() OVER (PARTITION BY EndID ORDER BY id)
FROM GetEndID
)
SELECT id, name, NameCount FROM GetCount WHERE OrderPrio = 1 ORDER BY id;
select distinct a.name,case when a.name = b.name then 2 else 1 end as cnt from
tb1 a
left outer join
tb1 b
on a.id = b.id+1
sQlfiddle
Click to see running

Resources