Recursive Query CTE Father - Son - Grandson error - sql

I have a table that has an ID and IDFATHER of some projects, these projects can receive N sons, so, the structure is like
ID
IDFATHER
REV
1
1
0
2
1
1
5
2
2
I need to, iniciating in ID 5 go to ID 1, so I did a CTE Query:
WITH lb (ID, IDFATHER) AS (
SELECT ID, IDFATHER
FROM PROJECTS
WHERE ID = 5
UNION ALL
SELECT I.ID, I.IDFATHER
FROM PROJECTS I
JOIN lb LBI ON I.ID = LBI.IDFATHER
--WHERE I.ID = LBI.IDFATHER -- Recursive Subquery
)
SELECT *
FROM lb
WHERE LB.ID = LB.IDFATHER
When this code runs it gives me:
The statement terminated. The maximum recursion 100 has been exhausted
before statement completion.
So basically I handle it by just adding:
SELECT TOP 1 * FROM LB WHERE LB.ID = LB.IDFATHER
But I really want to know were is my error. Can anyone give me a hand on these?

The first row points to itself so the recursion never stops. You need to add this condition inside the recursive cte:
WHERE LBI.ID <> LBI.IDFATHER
I would rather set IDFather of the first row to NULL.

The recursion didn't stop because your top row refers to itself endlessly.
If the top row has a null parent, that would have stopped the recursion.
Another approach is to use that case id = parentid as the termination logic.
The fiddle
WITH LB (ID, IDFATHER, idstart) AS (
SELECT ID, IDFATHER, id
FROM PROJECTS WHERE ID = 5
UNION ALL
SELECT I.ID, I.IDFATHER, lbi.idstart
FROM PROJECTS I
JOIN LB LBI
ON I.ID = lbi.IDFATHER
AND lbi.id <> lbi.idfather
)
SELECT id AS idtop, idstart
FROM LB
WHERE LB.ID = LB.IDFATHER
;
The result:

Related

SQL hide rows specifically

This is my table structure and sample rows:
And I have a SQL query like this :
WITH Test(ObjId, ObjectIcerik, KeyMi) AS
(
SELECT
ObjId, ObjectIcerik, KeyMi
FROM
Tek
WHERE
ObjId = 8
UNION ALL
SELECT
T.ObjId, T.ObjectIcerik, T.KeyMi
FROM
Tek T
INNER JOIN
Test as TE ON T.ObjParent = TE.ObjId
)
SELECT *
FROM Test
This is the result :
But I also need to hide parent and child rows if parent's KeyMi column is '1'.
What do I need to change in the SQL query to do this?
You should be able to just add the condition to the recursive CTE:
WITH Test(ObjId, ObjectIcerik, KeyMi) AS (
SELECT ObjId, ObjectIcerik, KeyMi FROM Tek
WHERE ObjId = 8 AND (KeyMI <> 1 OR KeyMI IS NULL)
UNION ALL
SELECT T.ObjId, T.ObjectIcerik, T.KeyMi
FROM Tek T INNER JOIN
Test TE
ON T.ObjParent = TE.ObjId
WHERE (t.KeyMI <> 1 OR t.KeyMI IS NULL)
)
SELECT *
FROM Test ;

T-SQL cursor or if or case when

I have this table:
Table_NAME_A:
quotid itration QStatus
--------------------------------
5329 1 Assigned
5329 2 Inreview
5329 3 sold
4329 1 sold
4329 2 sold
3214 1 assigned
3214 2 Inreview
Result output should look like this:
quotid itration QStatus
------------------------------
5329 3 sold
4329 2 sold
3214 2 Inreview
T-SQL query, so basically I want the data within "sold" status if not there then "inreview" if not there then "assigned" and also at the same time if "sold" or "inreview" or "assigned" has multiple iteration then i want the highest "iteration".
Please help me, thanks in advance :)
This is a prioritization query. One way to do this is with successive comparisons in a union all:
select a.*
from table_a a
where quote_status = 'sold'
union all
select a.*
from table_a a
where quote_status = 'Inreview' and
not exists (select 1 from table_a a2 where a2.quoteid = a.quoteid and a2.quotestatus = 'sold')
union all
select a.*
from table_a a
where quote_status = 'assigned' and
not exists (select 1
from table_a a2
where a2.quoteid = a.quoteid and a2.quotestatus in ('sold', 'Inreview')
);
For performance on a larger set of data, you would want an index on table_a(quoteid, quotestatus).
You want neither cursors nor if/then for this. Instead, you'll use a series of self-joins to get these results. I'll also use a CTE to simplify getting the max iteration at each step:
with StatusIterations As
(
SELECT quotID, MAX(itration) Iteration, QStatus
FROM table_NAME_A
GROUP BY quotID, QStats
)
select q.quotID, coalesce(sold.Iteration,rev.Iteration,asngd.Iteration) Iteration,
coalesce(sold.QStatus, rev.QStatus, asngd.QStatus) QStatus
from
--initial pass for list of quotes, to ensure every quote is included in the results
(select distinct quotID from table_NAME_A) q
--one additional pass for each possible status
left join StatusIterations sold on sold.quotID = q.quotID and sold.QStatus = 'sold'
left join StatusIterations rev on rev.quotID = q.quotID and rev.QStatus = 'Inreview'
left join StatusIterations asngd on asngd.quotID = q.quotID and asngd.QStatus = 'assigned'
If you have a table that equates a status with a numeric value, you can further improve on this:
Table: Status
QStatus Sequence
'Sold' 3
'Inreview' 2
'Assigned' 1
And the code becomes:
select t.quotID, MAX(t.itration) itration, t.QStatus
from
(
select t.quotID, MAX(s.Sequence) As Sequence
from table_NAME_A t
inner join Status s on s.QStatus = t.QStatus
group by t.quotID
) seq
inner join Status s on s.Sequence = seq.Sequence
inner join table_NAME_A t on t.quotID = seq.quotID and t.QStatus = s.QStatus
group by t.quoteID, t.QStatus
The above may look like complicated at first, but it can be faster and it will scale easily beyond three statuses without changing the code.

SQL Update Skipping duplicates

Table 1 looks like the following.
ID SIZE TYPE SERIAL
1 4 W-meter1 123456
2 5 W-meter2 123456
3 4 W-meter 585858
4 4 W-Meter 398574
As you can see. Items 1 and 2 both have the same Serial Number. I have an innerjoin update statement that will update the UniqueID on these devices based on linking their serial number to the list.
What I would like to do. Is modify by hand the items with duplicate serial numbers and scripted update the ones that are unique. Im presuming I have to reference the distinct command here somewhere buy not sure.
This is my update statement as is. Pretty simple and straight forward.
update UM00400
Set um00400.umEquipmentID = tb2.MIUNo
from UM00400 tb1
inner join AA_Meters tb2 on
tb1.umSerialNumber = tb2.Old_Serial_Num
where tb1.umSerialNumber <> tb2.New_Serial_Num
;WITH CTE
AS
(
SELECT * , rn = ROW_NUMBER() OVER (PARTITION BY SERIAL ORDER BY SERIAL)
FROM UM00400
)
UPDATE CTE
SET CTE.umEquipmentID = tb2.MIUNo
inner join AA_Meters tb2
on CTE.umSerialNumber = tb2.Old_Serial_Num
where tb1.umSerialNumber <> tb2.New_Serial_Num
AND CTE.rn = 1
This will update the 1st record of multiple records with the same SERIAL.
If i understand your question correctly below query will help you out :
;WITH CTE AS
(
// getting those serial numbers which are not duplicated
SELECT umSerialNumber,COUNT(umSerialNumber) as CountOfSerialNumber
FROM UM00400
GROUP BY umSerialNumber
HAVING COUNT(umSerialNumber) = 1
)
UPDATE A SET A.umEquipmentID = C.MIUNo
FROM UM00400 A
INNER JOIN CTE B ON A.umSerialNumber = B.umSerialNumber
INNER JOIN AA_Meters C ON A.umSerialNumber = C.Old_Serial_Num

SQL Query to find Parent-Child Child-Parent relationships?

o_O
If I have the following records in a table:
Parent Child
1 2 <--
2 1 <--
3 2
3 4
etc...
And I want to identify records that are both the parent of their child AND the child of their parent such as the 2 records identified by arrows above, how would I accomplish this?
I am trying to run some recursive SQL on this table, but these items are causing an infinite loop. I would like to identify these items so they can be addressed manually.
My brain is fried-enough from messing with recursive queries, I have nothing left to solve this one. Please help :)
If understood you well, you don't need recursion at all:
SELECT a.parent, a.child
FROM table1 a
INNER JOIN table1 b ON (b.child=a.parent and a.child = b.parent)
You might want to use LEFT JOIN instead of INNER if you also need to display rows that don't satisfy condition .
The following query will work in your example case. If it needs more you'll have to extend the demonstration information
;WITH CTE_DATA AS (
Select Parent = 1, Child = 2
union Select Parent = 2, Child = 1
union Select Parent = 3, CHild = 2
union Select Parent = 3, Child = 4
)
select
d1.*
from
CTE_DATA d1
join CTE_DATA d2 on d1.Child = d2.Parent and d2.Child = d1.Parent
DECLARE #YourTable TABLE (Parent INT, Child INT)
INSERT INTO #YourTable
SELECT 1, 2
UNION
SELECT 2, 1
UNION
SELECT 3, 2
UNION
SELECT 3, 4
SELECT *
FROM #YourTable A
INNER JOIN #YourTable B
ON A.Parent = B.Child AND A.Child = B.Parent

Recursive Query on a self referential table (not hierarchical)

I am creating a state chart of sorts with the data being stored in a simple self referencing table (JobPath)
JobId - ParentJobId
I was using a standard SQL CTE to get the data out which was working perfectly until I ended up with the following data
JobId - ParentId
1 2
2 3
3 4
4 2
Now as you can see Job 4 links to Job 2 which goes to Job 3 and then to Job 4 and so on.
Is there any way I can tell my query not to pull out data it already has?
Here is my current query
WITH JobPathTemp (JobId, ParentId, Level)
AS
(
-- Anchor member definition
SELECT j.JobId, jp.ParentJobId, 1 AS Level
FROM Job AS j
LEFT OUTER JOIN dbo.JobPath AS jp
ON j.JobId = jp.JobId
where j.JobId=1516
UNION ALL
-- Recursive member definition
SELECT j.JobId, jp.ParentJobId, Level + 1
FROM dbo.Job as j
INNER JOIN dbo.JobPath AS jp
ON j.JobId = jp.JobId
INNER JOIN JobPathTemp AS jpt
ON jpt.ParentId = jp.JobId
WHERE jp.ParentJobId <> jpt.JobId
)
-- Statement that executes the CTE
SELECT * FROM JobPathTemp
If you are not dealing with a large number of entries, the following solution might be suitable. The idea is to build the complete "id path" for each row and make sure the "current id" (in the recursive part) is not already in the path being processed:
(I removed the join to jobpath for testing purposes but the basic pattern should be the same)
WITH JobPathTemp (JobId, ParentId, Level, id_path)
AS
(
SELECT jobid,
parentid,
1 as level,
'|' + cast(jobid as varchar(max)) as id_path
FROM job
WHERE jobid = 1
UNION ALL
SELECT j.JobId,
j.parentid,
Level + 1,
jpt.id_path + '|' + cast(j.jobid as varchar(max))
FROM Job as j
INNER JOIN JobPathTemp AS jpt ON j.jobid = jpt.parentid
AND charindex('|' + cast(j.jobid as varchar), jpt.id_path) = 0
)
SELECT *
FROM JobPathTemp
;
This solution doesn't work, SQL Server doesn't support using UNION to join together the recursive term. Since you can't refer to the the recursion except as the join, tbh I don't see any alternative to using a stored function...
You didn't post your query... but I tried (in postgres, which works in much the same way) and if you use "UNION" (not "UNION ALL") in the recursive term, then it should automatically remove duplicate rows:
with /*recursive*/ jobs as
(select jobpath.jobid, jobpath.parentjobid from jobpath where jobid = 1
union
select jobpath.jobid, jobpath.parentjobid
from jobpath
join jobs on jobs.parentjobid = jobpath.jobid
)
select jobpath.* from jobpath join jobs on jobpath.jobid = jobs.jobid;