Parent row missing in child parent relationship in with CTE - sql-server-2005

i have a temporary table in which i have the following data , i want to filter the rows of child with his parent categoryID untill its reaches at the top of that Parent in those hierarchy .
;with cte (rowid,ParentCategoryID,CategoryID,Status,Level,CategoryName,ISProduct) as
(
Select rowid,ParentCategoryID,CategoryID,Status,Level,CategoryName,ISProduct from #newtemp where ParentCategoryId!=0
union all
select cte.rowid,cte.ParentCategoryID,cte.CategoryID,cte.Status,cte.Level,cte.CategoryName,cte.ISProduct
from #newtemp inner join cte ON cte.CategoryId=#newtemp.ParentCategoryId
)
select * from cte

You need replace cte.CategoryId=#newtemp.ParentCategoryId on c.ParentCategoryId = #newtemp.CategoryID
;with cte (rowid,ParentCategoryID,CategoryID,Status,Level,CategoryName,ISProduct) as
(
Select rowid, ParentCategoryID, CategoryID, Status, Level, CategoryName, ISProduct
from #newtemp
where ParentCategoryId!=0
union all
select t.rowid, t.ParentCategoryID, t.CategoryID, t.Status, t.Level, t.CategoryName, t.ISProduct
from #newtemp t inner join cte c ON c.ParentCategoryId = t.CategoryID
)
select * from cte
Demo on SQLFiddle

If I understand you correct. You what something like this:
First some test data:
DECLARE #tbl TABLE
(
rowid INT,
parentCategoryID INT,
CategoryID INT,
[Status] INT,
[Level] INT,
CategoryName VARCHAR(100),
ISProduct BIT
)
INSERT INTO #tbl
SELECT 1,0,1,1,0,'jewellary',1 UNION ALL
SELECT 2,0,2,1,0,'f',0 UNION ALL
SELECT 11,2,4,1,10,'ghdf',1
Then the CTE like this:
;WITH cte_name (rowid,CategoryID,parentCategoryID,HasChildren)
AS
(
SELECT
tbl.rowid,
tbl.CategoryID,
tbl.parentCategoryID,
CASE WHEN EXISTS
(
SELECT
NULL
FROM
#tbl AS tblInner
WHERE
tblInner.parentCategoryID=tbl.CategoryID
)
THEN 1
ELSE 0
END
AS HasChildren
FROM
#tbl AS tbl
WHERE
tbl.parentCategoryID=0
UNION ALL
SELECT
tbl.rowid,
tbl.CategoryID,
tbl.parentCategoryID,
cte.HasChildren
FROM
#tbl AS tbl
JOIN cte_name AS cte
on cte.CategoryID=tbl.parentCategoryID
)
SELECT
tbl.*
FROM
cte_name
JOIN #tbl AS tbl
ON cte_name.rowid=tbl.rowid
WHERE
cte_name.HasChildren=1

Related

Using a recursive CTE in a view

Having a typical parent/child hierarchy table it's a common thing to query it using Common Table Expression:
with CTE as (
select Id, ProviderId, ConsumerId
from T1
where ProviderId in (2, 3, 9)
union all
select T1.Id, T1.ProviderId, T1.ConsumerId
from T1
join CTE on C.ProviderId = CTE.ConsumerId
)
select * from CTE
Is it possible to create a view based on this query so that one can do:
select * from MagicView where ProviderId in (2,3,9)
In other words, can we somehow extract parameters from the anchor part of the CTE to create a generic view?
Create a TVF:
CREATE FUNCTION my_function (
#ProviderId int
)
RETURNS #ProviderTable TABLE
(
Id int NULL,
ProviderId int NULL,
ConsumerId int NULL
)
AS
BEGIN
WITH cte AS (
SELECT Id,
ProviderId,
ConsumerId
FROM T1
WHERE ProviderId in (#ProviderId)
UNION ALL
SELECT t.Id,
t.ProviderId,
t.ConsumerId
FROM T1 t
INNER JOIN cte c
ON t.ProviderId = c.ConsumerId
)
INSERT INTO #ProviderTable
SELECT * FROM cte;
RETURN;
END;
Than create a view:
CREATE VIEW my_view
AS
SELECT m.*
FROM Providers p
CROSS APPLY my_function (p.ProviderId) m
After that you can SELECT from view whatever you need:
SELECT *
FROM my_view
WHERE ProviderId in (2,3,9)
OPTION (MAXRECURSION 0)
One way to do Table Valued Route
CREATE FUNCTION [dbo].[MVParam_tvf](#ParameterString nvarchar(4000), #Delimiter char(1)= ',')
RETURNS #VALUES TABLE (Param nvarchar(4000))AS
Figure out your code preference to spilt delimited string to table
END
CREATE FUNCTION dbo.MagicView_tvf
(
#ParameterString NVARCHAR(4000)
,#Delimiter CHAR(1)
)
RETURNS TABLE
AS
RETURN
(
with CTE as (
select Id, ProviderId, ConsumerId
from
T1 t
INNER JOIN MVParam_tvf (#ParameterString, #Delimiter) p
ON CAST(t.ProviderId AS VARCHAR(10)) = p.OutPutColumn
union all
select T1.Id, T1.ProviderId, T1.ConsumerId
from T1
join CTE on C.ProviderId = CTE.ConsumerId
)
select * from CTE
)
GO
SELECT * FROM dbo.MagicView_tvf ('2,3,9')
If you want to go the view route create the view without the where line on anchor part of cte and then when you call it write your where statement on that query.

TSQL create ragged hierarchy from flat structure

Consider this table:
declare #table as table (
GL_Group_Code varchar(8),
GL_Branch_Code varchar(8),
Account_Number varchar(4),
GL_Centre_Number varchar(6)
)
insert into #table
select '0100', '0110', '1200', 'S10118' union
select '0100', '0110', '1580', 'S16053' union
select '0200', '0210', '2050', '200049' union
select '0200', '0210', '2050', '782844'
The desired outcome is a parent child relationship across the four source columns into:
declare #table_hier as table (
Parent_ID varchar(8),
Item_ID varchar(8)
)
With a relationship order of column 1,2,3,4 the result for the first row from the source table would look like:
null 0100
0100 0110
0110 1200
1200 S10118
The second row would be:
null 0100
0100 0110
0110 1580
1580 S16053
The relationship order can vary: 1,2,4,3 or 3,4 or 1,3,4
I'm thinking recursive CTE however late on a Friday I could do with little help.
IF you can have id in your table and use table variable to store columns you want to get, you can do this:
declare #ord table (id int)
insert into #ord
values (1), (2), (4)
;with cte as (
select
t.id,
c.value,
row_number() over(partition by t.id order by c.id) as rn
from #table as t
cross apply (
values
(1, t.GL_Group_Code),
(2, t.GL_Branch_Code),
(3, t.Account_Number),
(4, t.GL_Centre_Number)
) as c(id, value)
where
exists (select * from #ord as o where o.id = c.id)
)
insert into #table_hier
select
c1.value as Parent_ID, c2.value as Item_ID
from cte as c1
inner join cte as c2 on c2.rn = c1.rn + 1 and c2.id = c1.id
order by c1.rn
insert into #table_hier
select null, t.Parent_ID
from #table_hier as t
where
not exists
(
select *
from #table_hier as t2
where t2.Item_ID = t.Parent_ID
)
select * from #table_hier
sql fiddle demo
Or if you can store your data in the tables or temporary tables, you can use dynamic SQL solution:
declare #ord table (id int)
declare #stmt nvarchar(max)
insert into #ord
values (1), (2), (4)
;with cte as (
select c.name, row_number() over(order by c.colid) as rn
from sys.syscolumns as c
where
c.id = object_id('dbo.table1') and
exists (select * from #ord as o where o.id = c.colid)
)
select #stmt =
isnull(#stmt + ', ', '') +
'(' + c1.name + ', ' + c2.name + ')'
from cte as c1
inner join cte as c2 on c2.rn = c1.rn + 1
order by c1.rn
select #stmt = '
insert into table_hier1
select C.Parent_ID, C.Item_ID
from table1 as t
cross apply (
values ' + #stmt + '
) as C(Parent_ID, Item_ID)
'
exec sp_executesql
#stmt = #stmt
insert into table_hier1
select null, t.Parent_ID
from table_hier1 as t
where
not exists
(
select *
from table_hier1 as t2
where t2.Item_ID = t.Parent_ID
)
select * from table_hier1
sql fiddle demo
try something like this
declare #table as table (
GL_Group_Code varchar(8),
GL_Branch_Code varchar(8),
Account_Number varchar(4),
GL_Centre_Number varchar(6)
)
insert into #table
select '0100', '0110', '1200', 'S10118' union
select '0100', '0110', '1580', 'S16053' union
select '0200', '0210', '2050', '200049' union
select '0200', '0210', '2050', '782844'
declare #table_hier as table (
Parent_ID varchar(8),
Item_ID varchar(8)
)
DECLARE #RelatonShip VARCHAR(4)
SET #RelatonShip = '134'
IF #RelatonShip = '1234'
BEGIN
INSERT INTO #table_hier ( Parent_ID, Item_ID )
SELECT GL_Group_Code, GL_Branch_Code FROM #table
UNION ALL SELECT GL_Branch_Code, Account_Number FROM #Table
UNION ALL SELECT Account_Number, GL_Centre_Number FROM #table
END
IF #RelatonShip = '1243'
BEGIN
INSERT INTO #table_hier ( Parent_ID, Item_ID )
SELECT GL_Group_Code, GL_Branch_Code FROM #table
UNION ALL SELECT GL_Branch_Code, GL_Centre_Number FROM #Table
UNION ALL SELECT GL_Centre_Number, Account_Number FROM #table
END
IF #RelatonShip = '34'
BEGIN
INSERT INTO #table_hier ( Parent_ID, Item_ID )
SELECT Account_Number, GL_Centre_Number FROM #table
END
IF #RelatonShip = '134'
BEGIN
INSERT INTO #table_hier ( Parent_ID, Item_ID )
SELECT GL_Group_Code, Account_Number FROM #table
UNION ALL SELECT Account_Number, GL_Centre_Number FROM #table
END
SELECT * FROM #table_hier

Select item type and its base type in SQL Server?

I have a simple table :
DECLARE #t TABLE(item INT, itemType INT )
insert INTO #t SELECT 1000,0
insert INTO #t SELECT 1000,3
insert INTO #t SELECT 1000,5
insert INTO #t SELECT 1000,6
insert INTO #t SELECT 2000,0
insert INTO #t SELECT 2000,3
insert INTO #t SELECT 2000,5
insert INTO #t SELECT 2000,6
insert INTO #t SELECT 3000,0
insert INTO #t SELECT 3000,10
insert INTO #t SELECT 4000,11
I want to select all items where itemtype = 3 but if there is a row , provide also its base itemtype (if it exists) which is itemType = 0.
For example :
for itemType = 3
1000,0 should be provided --why ? because table also has 1000 + itemType 0
1000,3 should be provided --why ? because we looked for itemType=3
2000,0 should be provided --why ? because table has found 2000,3 and this 2000 also has itemType=0
2000,3 should be provided --why ? because we looked for itemType=3
for itemType = 10
3000,0 should be provided --why ? because table has found 3000,10 and this 3000 also has itemType=0
3000,10 should be provided --why ? because we looked for itemType=10
for itemType = 11
4000,11 should be provided --why ? because we looked for itemType=11 ( notice , there isn't itemType 0 , so only itself).
I started doing :
;with cte as(
SELECT * FROM #t
)
select * from cte where itemType=3
In summary, if the itemType is found, provide itself + its zero type (if exists), and also for his siblings ( sample(#1) )
But I can't do union here cause CTE is not recognized there... rubbish. it is possible.
How can I solve it ?
SQL ONLINE
To avoid evaluating the itemType = 3 query twice you can self outer join then use CROSS APPLY ... VALUES to UNPIVOT
DECLARE #itemType INT = 3;
WITH T(item1, itemType1, item2, itemType2 )
AS (SELECT *
FROM #t T1
LEFT JOIN #t T2
ON T1.item = T2.item
AND T2.itemType = 0
AND T1.itemType <> 0
WHERE T1.itemType = #itemType)
SELECT item,
itemType
FROM T
CROSS APPLY ( VALUES (item1, itemType1),
(item2, itemType2) ) v(item, itemType)
WHERE item IS NOT NULL
SQL Fiddle
Execution Plans
DECLARE #findtype INT = 3;
WITH results AS
(
SELECT t.item, #findtype
FROM #t t
WHERE t.itemType = #findtype
UNION ALL
SELECT t.item, 0
FROM #t t
INNER JOIN results r on r.item = t.item
WHERE t.itemType = 0
)
SELECT *
FROM results;
WITH recordList
AS
(
SELECT item, itemType
FROM SampleTable
WHERE itemType = 11 -- change here
)
SELECT item, itemType FROM recordList
UNION
SELECT a.item, a.itemType
FROM SampleTable a
INNER JOIN recordList b
ON a.item = b.item
WHERE a.itemtype = 0
SQLFiddle Demo
SQLFiddle Demo (using IN clause for multiple values)

SQL Query using distinct and max

I have a dataset like:
type seqID text
A 1 Text1a
A 2 Text2a
A 3 Text3a
B 1 Text1b
B 2 Text2b
How do I get the row back by type with the highest seqID grouped by type? So in the above example I would want the row that has A, 3, Text3a and B, 2, Text2b returned.
SELECT *
FROM tmp t1
WHERE NOT EXISTS
(SELECT 1 FROM tmp t2 WHERE t1.type = t2.type AND t2.seqID > t1.seqID)
It shouldn't exists any other row with the same type and higher seqID.
You kind of need an ID, but since "Text" seems unique for this example
CREATE TABLE #TMP
(type VARCHAR(3), seqID INT, [text] varchar(256))
insert #TMP values ('A' , 1 , 'Text1a')
insert #TMP values ('A' , 2 , 'Text2a')
insert #TMP values ('A' , 3 , 'Text3a')
insert #TMP values ('B' , 1 , 'Text1b')
insert #TMP values ('B' , 2 , 'Text2b')
SELECT * FROM #TMP T
where [text] IN
(SELECT TOP 1 [text] FROM #TMP t2 WHERE t.type = t2.type ORDER BY t2.seqID DESC)
SELECT tbl.*
FROM
( SELECT type, MAX(seqID)
FROM tbl
GROUP BY type) maxes
WHERE
tbl.type= maxes.type AND
tbl.seqID= maxes.seqID
SELECT t.* FROM
(
SELECT type, MAX(seqID) as maxId
FROM Table
GROUP BY type
) m
INNER JOIN Table t ON m.maxId = t.seqId
Using CTE
;WITH maxIds(maxId)
AS
(
SELECT type, MAX(seqID) as maxId
FROM Table
GROUP BY type
)
SELECT t.* FROM
Table t
INNER JOIN maxIds m ON m.maxId = t.seqID
If you are on SQL Server 2005+, you could use a ranking function (more specifically, ROW_NUMBER()):
SELECT
type,
seqID,
text
FROM (
SELECT
*,
rnk = ROW_NUMBER() OVER (PARTITION BY type ORDER BY seqID DESC)
FROM atable
) s
WHERE rnk = 1
create table #tlb1(
[type] VARCHAR(3), seqID INT, [text] varchar(max)
)
declare #type varchar(3), #text varchar(max);
declare #seqID int;
declare seq_cursor cursor for
select [type], max(seqID) from tbl group by [type]
open seq_cursor
fetch next from seq_cursor into #type,#seqID
while(##fetch_status=0)
begin
set #text= (select [text] from tbl where [type]=#type and seqID=#seqid);
insert into #tlb1 values (#type, #seqID,#text);
fetch next from seq_cursor into #type,#seqID
end
select * from #tlb1
close seq_cursor
deallocate seq_cursor
truncate table #tlb1
Try:
SELECT type, max(seqID),text
FROM 'db'
GROUP BY type
As easy as that.
EDITED solution. Consider this a psuedo-code (since I am not familiar with SQL server syntax):
SELECT a.type, a.seqID, a.text FROM table a
JOIN
(SELECT type, max(seqID) seqID FROM table GROUP BY type) b
ON a.seqID = b.seqID AND a.type=b.type

t-sql recursive query

Based on an existing table I used CTE recursive query to come up with following data. But failing to apply it a level further.
Data is as below
id name parentid
--------------------------
1 project 0
2 structure 1
3 path_1 2
4 path_2 2
5 path_3 2
6 path_4 3
7 path_5 4
8 path_6 5
I want to recursively form full paths from the above data. Means the recursion will give the following output.
FullPaths
-------------
Project
Project\Structure
Project\Structure\Path_1
Project\Structure\Path_2
Project\Structure\Path_3
Project\Structure\Path_1\path_4
Project\Structure\Path_2\path_5
Project\Structure\Path_3\path_6
Thanks
Here's an example CTE to do that:
declare #t table (id int, name varchar(max), parentid int)
insert into #t select 1, 'project' , 0
union all select 2, 'structure' , 1
union all select 3, 'path_1' , 2
union all select 4, 'path_2' , 2
union all select 5, 'path_3' , 2
union all select 6, 'path_4' , 3
union all select 7, 'path_5' , 4
union all select 8, 'path_6' , 5
; with CteAlias as (
select id, name, parentid
from #t t
where t.parentid = 0
union all
select t.id, parent.name + '\' + t.name, t.parentid
from #t t
inner join CteAlias parent on t.parentid = parent.id
)
select *
from CteAlias
Try something like this:
WITH Recursive AS
(
SELECT
ID,
CAST(PathName AS VARCHAR(500)) AS 'FullPaths',
1 AS 'Level'
FROM
dbo.YourTable
WHERE
ParentID = 0
UNION ALL
SELECT
tbl.ID,
CAST(r.FullPaths + '\' + tbl.PathName AS VARCHAR(500)) AS 'FullPaths',
r.Level + 1 AS 'Level'
FROM
dbo.YourTable tbl
INNER JOIN
Recursive r ON tbl.ParentID = r.ID
)
SELECT * FROM Recursive
ORDER BY Level, ID
Output:
ID FullPaths Level
1 project 1
2 project\structure 2
3 project\structure\path_1 3
4 project\structure\path_2 3
5 project\structure\path_3 3
6 project\structure\path_1\path_4 4
7 project\structure\path_2\path_5 4
8 project\structure\path_3\path_6 4
try this:
DECLARE #YourTable table (id int, nameof varchar(25), parentid int)
INSERT #YourTable VALUES (1,'project',0)
INSERT #YourTable VALUES (2,'structure',1)
INSERT #YourTable VALUES (3,'path_1',2)
INSERT #YourTable VALUES (4,'path_2',2)
INSERT #YourTable VALUES (5,'path_3',2)
INSERT #YourTable VALUES (6,'path_4',3)
INSERT #YourTable VALUES (7,'path_5',4)
INSERT #YourTable VALUES (8,'path_6',5)
;WITH Rec AS
(
SELECT
CONVERT(varchar(max),nameof) as nameof,id
FROM #YourTable
WHERE parentid=0
UNION ALL
SELECT
CONVERT(varchar(max),r.nameof+'\'+y.nameof), y.id
FROM #yourTable y
INNER jOIN Rec r ON y.parentid=r.id
)
select * from rec
output:
nameof
-----------------------------------------------
project
project\structure
project\structure\path_1
project\structure\path_2
project\structure\path_3
project\structure\path_3\path_6
project\structure\path_2\path_5
project\structure\path_1\path_4
(8 row(s) affected)
Something like
;WITH MyCTE AS
(
SELECT
name AS FullPaths, id
FROM
MyTable
WHERE
parentid = 0 /*Normally it'd be IS NULL with an FK linking the 2 columns*/
UNION ALL
SELECT
C.FullPaths + '\' + M.name, M.id
FROM
MyCTE C
JOIN
MyTable M ON M.parentid = C.id
)
SELECT FullPaths FROM MyCTE
You'll have to change the name of #test table I was using.
WITH cte(id, name, parentid) AS
(
SELECT id, convert(varchar(128), name), parentid
FROM #test
WHERE parentid = 0
UNION ALL
SELECT t.id, convert(varchar(128), c.name +'\'+t.name), t.parentid
FROM #test t
INNER JOIN cte c
ON c.id = t.parentid
)
SELECT name as FullPaths
FROM cte
order by id