Recursive CTE help - how do you code this non-hierarchal sequence? - sql-server

I'm trying to write a recursive CTE for a table that does not have a hierarchy. Meaning that there is no NULL in the family of IDs that are related.
For example table looks like this:
So it looks like this:
AccountID Account_RelationshipID
--------------------------------
1 2
2 4
4 6
6 11
11 1
15 17
17 19
19 15
So 1 relates to 2. 2 relates to 4. 4 relates to 6. 6 relates to 11. And then 11 loops back to ID of 1.
Then there is a new family. 15 relates to 17. 17 relates to 19. and then 19 goes back to 15.
There is also a separate Account_Detail table that has the account date:
AccountID AccountName AccountDate
-------------------------------------
1 Dave 1/1/2012
2 Dave 1/1/2013
4 Dave 1/1/2014
6 Dave 1/1/2015
11 Dave 1/1/2016
15 Paul 7/1/2015
17 Paul 7/1/2016
19 Paul 7/1/2017
I tried writing this as my code:
WITH C AS
(
SELECT
AR.AccountID,
AR.Account_RelationshipID,
AD.AccountDate
FROM
Account_Relationship AR
INNER JOIN
Account_Detail AD ON AD.AccountID = AR.AccountID
UNION ALL
SELECT
AR2.AccountID,
AR2.Account_RelationshipID,
AD.AccountDate
FROM
Account_Relationship AR2
INNER JOIN
Account_Detail AD2 ON AD2.Account_ID = AR2.Account_ID
INNER JOIN
C ON C.AccountID = AR2.Account_relationshipID
WHERE
AD.AccountDate < AD2.AccountDate
)
Obviously this code is totally wrong. This is as far as I've gotten. This code will loop infinitely.
I was thinking I could break the loop by adding a function that states when the AccountDate of the next AccountID in the loop is less than the AccountDate of the last AccountID, to break the loop and go to the next one.
Also, how do you get it to go to the next "family" of accountIDs in the loops (Paul in this case)? All the videos and tutorials I've seen about Recursive CTEs just teach how to do it for one family - usually with a hierarchical structure that breaks at NULL as well.
Help!!

Related

Query min column header while excluding blanks and handling duplicates

I have the following table.
Name
Score A
Score B
Score C
Bob
8
6
Sue
9
12
9
Joe
11
2
Susan
7
9
10
Tim
10
12
4
Ellie
9
8
7
In my actual table there are about 2k rows.
I am trying to get the min score (excluding blanks & handles duplicate scores) for each person into another column using the QUERY formula or ARRAYFORMULA, really to avoid entering a formula for each row.
As I do currently have this
=INDEX($B$1:$D$1,MATCH(MIN(B2:D2),B2:D2,0))
But that involves dragging down through each cell, as I do this on a few sheets that have circa 2k rows, it's very slow when inputting new data.
This should be the end result
Name
Score A
Score B
Score C
Min Score
Bob
8
6
Score C
Sue
9
12
9
Score A
Joe
11
2
Score B
Susan
7
9
10
Score A
Tim
10
12
4
Score C
Ellie
9
8
7
Score C
use:
=INDEX(SORTN(SORT(SPLIT(QUERY(FLATTEN(
IF(B2:D="",,B1:D1&"×"&B2:D&"×"&ROW(B2:D))),
"where Col1 is not null", ),
"×"), 3, 1, 2, 1), 9^9, 2, 3, 1),, 1)
The following answer employs three of the newest set of functions that are still being rolled out by Google so you might not be able to use it right now, but in a few weeks when they're fully rolled out you definitely will (this worked using the Android version of Sheets just now for me):
=arrayformula(if(len(A2:A),byrow(B2:D,lambda(row,xlookup(min(row),row,B1:D1))),))
Assuming the names are in column A, this should give a result for every row which has a name in it. I'm sure there are other ways of doing this, but these 'row/column-wise' problems are really ideal use-cases for LAMBDA and its helper functions like BYROW.

how to count classses in columns

I'm trying to make a query and i'm having a bad time with one thing. Suppose I have a table that looks like this:
id
Sample
Species
Quantity
Group
1
1
AA
5
A
2
1
AB
6
A
3
1
AC
10
A
4
1
CD
15
C
5
1
CE
20
C
6
1
DA
13
D
7
1
DB
7
D
8
1
EA
6
E
9
1
EF
4
E
10
1
EB
2
E
In the table I filter to have just 1 sample (but i have many), it has the species, the quantity of that species and a functional group (there are only five groups from A to E). I would like to make a query to group by the samples and make columns of the counts of the species of certain group, something like this:
Sample
N_especies
Group A
Group B
Group C
Group D
Group E
1
10
3
0
2
2
3
So i have to count the species (thats easy) but i don't know how to make the columns of a certain group, can anyone help me?
You can use PIVOT :
Select a.Sample,[A],[B],[C],[D],[E], [B]+[A]+[C]+[D]+[E] N_especies from
(select t.Sample,t.Grp from [WS_Database].[dbo].[test1] t) t
PIVOT (
COUNT(t.Grp)
for t.Grp in ([A],[B],[C],[D],[E])
) a

T-SQL to sum total value instead of rejoining table multiple times

I've looked for an example question like this, I ask for grace if it's been answered (I thought it would have been but have a hard time finding meaningful results with the terms I searched.)
I work at a manufacturing plant where at ever manufacturing operation a part is issued a new serial number. The database table I have to work with has the serial number recorded in the Container field and the previous serial number the part had recorded in the From_Container field.
I'm trying to SUM the Extended_Cost column on parts we've had to re-do operations on.
Here's a sample of data from tbl_Container:
Container From_Container Extended_Cost Part_Key Operation
10 9 10 PN_100 60
9 8 10 PN_100 50
8 7 10 PN_100 40
7 6 10 PN_100 30
6 5 10 PN_100 20
5 4 10 PN_100 50
4 3 10 PN_100 40
3 2 10 PN_100 30
2 1 10 PN_100 20
1 100 10 PN_100 10
In this example the SUM I would expect returned is 40, because operations 20, 30, 40 and 50 were all re-done and cost $10 each.
So far I've been able to do this by rejoining the table to itself 10 times using aliases in the following fashion:
LEFT OUTER JOIN tbl_Container AS FCP_1
ON tbl_Container.From_Container = FCP_1.Container
AND FCP_1.Operation <= tbl_Container.Operation
AND tbl_Container.Part_Key = FCP_1.Part_Key
And then using SUM to add the Extended_Cost fields together. However, I'm violating the DRY principle and there has got to be a better way.
Thank you in advance for your help,
Me
You can try this query.
;WITH CTE AS
(
SELECT TOP 1 *, I = 0 FROM tbl_Container C ORDER BY Container
UNION ALL
SELECT T.*, I = I + 1 FROM CTE
INNER JOIN tbl_Container T
ON CTE.Container = T.From_Container
AND CTE.Part_Key = T.Part_Key
)
SELECT Part_Key, SUM(T1.Extended_Cost) Sum_Extended_Cost FROM CTE T1
WHERE
EXISTS( SELECT * FROM
CTE T2 WHERE
T1.Operation = T2.Operation
AND T1.I > T2.I )
GROUP BY Part_Key
Result:
Part_Key Sum_Extended_Cost
---------- -----------------
PN_100 40

Wrong predicted SQL Server query outcome

I'm learning for an SQL server exam and I got these questions I answered wrong, but I don't understand why the right answers are right and how you get these.
nr naam aantal chef
1 Anouk 14 2
2 Hans 14 NULL
3 Ali 13 5
4 Kees 12 5
5 Ben 3 2
nr soort stad chef
1 cursus DenHaag 2
2 cursus Amsterdam NULL
3 congres NewYork 5
4 lezing Utrecht 5
nr werknemer reis aantal datum bedrag
01 1 4 8 17-04-2013 420,56
02 3 3 5 05-04-2013 825,80
03 1 1 5 10-04-2013 140,00
04 null 2 2 10-04-2013 156,75
05 4 4 8 17-04-2013 328,90
06 5 3 5 05-04-2013 560,45
The 2 questions are:
a.
SELECT naam
FROM werknemer
WHERE nr NOT IN (SELECT werknemer
FROM declaratie);
b.
SELECT naam, COUNT(*)
FROM werknemer w LEFT OUTER JOIN declaratie d ON w.nr = d.werknemer
GROUP BY naam;
My answers are:
a: Hans
b: naam count(*)
Anouk 2
Hans 0
Ali 1
Kees 1
Ben 1
But the right answers are:
a: none
b: naam count(*)
Anouk 2
Hans 1
Ali 1
Kees 1
Ben 1
Can someone explain me what I probably missed?
a) is because performing any kind of NOT IN (1,NULL,3,4,5) returns a NULL results set because SQL cannot say whether any given value is not equal to NULL and thus effectively returns "I don't know". There is an excellent explanation of this on Stack Overflow.
The reason b) is wrong is that you are LEFT JOINing from werknemer. Thus all rows from werknemer are returned - including Hans. COUNT(*) will return a count of rows pertaining to that individual so Hans returns 1. Were you to COUNT(d.werknemer) then Hans would be 0 as all declaratie columns would be NULL for Hans because the JOIN predicate would not be met.
It's very likely that what you miss is that there is no way to compare NULL and INT using '='. This makes your result different, you are considering that comparing 2 (Hans) with NULL from declaratie will return false. You should try running this query to see what do I mean:
if 1=null or 1=1 print 'true' else print 'false'
if 1=null and 1=1 print 'true' else print 'false'
Ok, so a is wrong cuz of the null.
You cant use in(1,2,3 ,null,5)
That will return a nulled resuld and you will recieve none.
For b, because you use the left join it means that you get the entire original table with all of the values of the second one. So it means that you get a row for hans but all nulls im the left side because there is no match (but you still count it as a row)

"ColumnName" is not a recognized table hints option. error while Joining a table value function with a query

I have a sample table like the one below
table1
chktid ToUser
1 3
2 6
3 3
4 1
chkid is the parentId and sometimes this parent has childs and each of them, has sub-childs and so on
I have written a function to return all the childs and sub-childs of the particular chktid. Calling my function dbo.hierychyreturnfn(chktid) with different chktid as input parameter, I am getting the following results
For chktid = 1 and so select * from dbo.hierychyreturnfn(1), the output is:
chkchildid
10
11
12
13
For chktid = 2 and so select * from dbo.hierychyreturnfn(2), the output is:
chkchildid
14
15
16
For chktid = 3 and so select * from dbo.hierychyreturnfn(3), the output is:
chkchildid
19
For chktid = 4 and so select * from dbo.hierychyreturnfn(4), it returns no rows since chkid=4 has no childs.
My question is: Is there any way to join the function and table 1 and return all results?
My expected out put is:
chkid ToUser chkchildid
1 3 10
1 3 11
1 3 12
1 3 13
2 6 14
2 6 15
2 6 16
3 3 19
I am trying to use cross apply for getting this
SELECT a.chkid
,a.ToUser
,b.chkchildid
FROM table1 a
CROSS APPLY dbo.hierychyreturnfn(1) b // need to join table1 and pass each chkid
But I don't know how to pass each chkid to hierychyreturnfn(). I am stuck here. I have also tried code below also but it gives an error
"chkid" is not a recognized table hints option. If it is intended as a
parameter to a table-valued function or to the CHANGETABLE function,
ensure that your database compatibility mode is set to 90.
SELECT a.chkid
,a.ToUser
,b.chkchildid
FROM table1 a
CROSS APPLY dbo.hierychyreturnfn(chkid) b
Please help me to solve this issue.

Resources