T/SQL - Trying to convert col to rows - sql

DDL/DML http://www.sqlfiddle.com/#!6/2d69c/1
Desired Output:
Country_ State_ FamilyTag ChildTag Name HouseHold IsReachable Rank_
US CA Family A Child A Ch A1 1 1 1
US CA Family B Child A Ch A1 1 1 2
US CA Family C Child A Ch A1 1 1 3
US CA Family B Child B Ch B1 3 1 1
US CA Family A Child B Ch B1 3 1 2
US CA Family C Child B Ch B1 3 1 3
US CA Family C Child C Ch C1 2 1 1
US CA Family A Child C Ch C1 2 1 3
US CA Family B Child C Ch C1 2 1 2
"Child[n]Tag" field data should go in "ChildTag" field.
"Child[n]Name" field data should go in "Name" field.
"ChildFamilyTag[n]" field data should go in "FamilyTag" field.
"ChildFamilyRank[n]" field data should go in "Rank_" field.
"Child[n]HouseHold" field data should go in "Household" field.
"Child[n]IsReachable" field data should go in "IsReachable" field.
Getting NULLs instead of above desired output...please help
Note:
# of "ChildFamily%" columns can vary (right now, it has 3 sets...but could be 2,3, and etc.
Also, my example shows only one record, there could be more. :)
Thank you
p.s. if you need me to post code here, kindly let me know.

Your query is pretty much along the way there. But you are missing conditions in the case statements. Adding new conditions seems pointless, when some arithmetic on N solves the problem:
SELECT *
FROM (SELECT Country_
, State_
, (CASE WHEN N = 1 THEN 1
WHEN N = 2 THEN 2
WHEN N = 3 THEN 3
WHEN N = 4 THEN 4
WHEN N = 5 THEN 5
WHEN N = 6 THEN 6
WHEN N = 7 THEN 7
WHEN N = 8 THEN 8
WHEN N = 9 THEN 9 END) AS ParentNo
, (CASE WHEN N = 1 THEN [Child1FamilyTag1]
WHEN N = 2 THEN [Child1FamilyTag2]
WHEN N = 3 THEN [Child1FamilyTag3]
WHEN N = 4 THEN [Child2FamilyTag1]
WHEN N = 5 THEN [Child2FamilyTag2]
WHEN N = 6 THEN [Child2FamilyTag3]
WHEN N = 7 THEN [Child3FamilyTag1]
WHEN N = 8 THEN [Child3FamilyTag2]
WHEN N = 9 THEN [Child3FamilyTag3] END) AS FamilyTag
, (CASE WHEN (N + 2) / 3 = 1 THEN [Child1Tag]
WHEN (N + 2) / 3 = 2 THEN [Child2Tag]
WHEN (N + 2) / 3 = 3 THEN [Child3Tag] END) AS ChildTag
, (CASE WHEN (N + 2) / 3 = 1 THEN [Child1Name]
WHEN (N + 2) / 3 = 2 THEN [Child2Name]
WHEN (N + 2) / 3 = 3 THEN [Child3Name] END) AS Name
, (CASE WHEN (N + 2) / 3 = 1 THEN [Child1HouseHold]
WHEN (N + 2) / 3 = 2 THEN [Child2HouseHold]
WHEN (N + 2) / 3 = 3 THEN [Child3HouseHold] END) AS HouseHold
, (CASE WHEN (N + 2) / 3 = 1 THEN [Child1IsReachable]
WHEN (N + 2) / 3 = 2 THEN [Child2IsReachable]
WHEN (N + 2) / 3 = 3 THEN [Child3IsReachable] END) AS IsReachable
, (CASE WHEN (N % 3) = 1 THEN [Child1FamilyRankA]
WHEN (N % 3) = 2 THEN [Child1FamilyRankB]
WHEN (N % 3) = 0 THEN [Child1FamilyRankC]
END) AS Rank
FROM Temp TI CROSS JOIN
(SELECT 1 AS N
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL
SELECT 9) N
) T;
Here is the SQL Fiddle.
EDIT (in response to comment):
Your original query is
, (CASE WHEN N = 1 THEN [Child1FamilyRankA]
WHEN N = 2 THEN [Child1FamilyRankB]
WHEN N = 3 THEN [Child1FamilyRankC]
WHEN N = 4 THEN [Child2FamilyRankA]
WHEN N = 2 THEN [Child2FamilyRankB]
WHEN N = 3 THEN [Child2FamilyRankC]
WHEN N = 7 THEN [Child3FamilyRankA]
WHEN N = 2 THEN [Child3FamilyRankB]
WHEN N = 3 THEN [Child3FamilyRankC] END) AS Rank_
I don't know why the values are arranged as they are for this rank_, but you can keep using them. I think I replaced them with something else.

Related

How to return matching column value where only all subqueries are true

I'd like to adjust the below SQL statement so that it only returns the PART_ID where all SELECT PART_ID FROM FABRICATION... conditions are met.
Currently the below statement would return part_id's 1 & 2, when the intent is to only return 1.
Thanks for any advice you can offer.
SELECT *
FROM Part
WHERE Section = 'C6x1_5/8x1/4'
AND Length BETWEEN 41.99 AND 42.01
AND Part.Part_ID IN (SELECT Part_ID FROM Fabrication
WHERE Type LIKE 'HOLE'
AND Face = 1
AND Side = 3
AND Location BETWEEN 2.99 AND 3.01
AND Offset = 2
AND Parameter_1 = 0.5625)
AND Part.Part_ID IN (SELECT Part_ID FROM Fabrication
WHERE Type LIKE 'HOLE'
AND Face = 1
AND Side = 3
AND Location BETWEEN 5.99 AND 6.01
AND Offset = 2
AND Parameter_1 = 0.5625)
Fabrication Table:
PART_ID
TYPE
FACE
SIDE
LOCATION
OFFSET
PARAMETER_1
PARAMETER_2
PARAMETER_3
1
HOLE
1
3
6
2
0.5625
0
0
1
HOLE
1
3
3
2
0.5625
0
0
2
HOLE
1
3
6
2
0.5625
0
0
2
HOLE
1
3
3
2
0.5625
0
0
2
HOLE
1
3
9
2
0.5625
0
0
3
HOLE
1
3
3
2
0.5625
0
0
You can use aggregation and HAVING to validate that there there is exactly one match for each condition and no others:
SELECT *
FROM Part
WHERE Section = 'C6x1_5/8x1/4' AND
Length BETWEEN 41.99 AND 42.01
Part.Part_ID IN (SELECT f.Part_ID
FROM Fabrication f
GROUP BY f.Part_ID
HAVING SUM(CASE WHEN f.Type = 'HOLE' AND f.Face = 1 AND f.Side = 3 AND f.Offset = 2 AND f.Parameter_1 = 0.5625 THEN 1 ELSE 0 END) = COUNT(*) AND
SUM(CASE WHEN Location BETWEEN 2.99 AND 3.01 THEN 1 ELSE 0 END) = 1 AND
SUM(CASE WHEN Location BETWEEN 5.99 AND 6.01 THEN 1 ELSE 0 END) = 1
);
It looks like the only variance in your two queries is Location, for which you want to ensure the number of Fabrication rows meet your criteria and do not have any additional rows outside of your criteria.
One option you can try uses conditional aggregation:
select *
from Part
where Section = 'C6x1_5/8x1/4'
and Length between 41.99 and 42.01
and Part.Part_ID in (
select PART_ID
from Fabrication
where
TYPE = 'HOLE'
and FACE = 1
and SIDE = 3
and offset = 2
and PARAMETER_1 = 0.5625
group by PART_ID
having Count(*)=Sum(case when LOCATION between 2.99 and 3.01 then 1 end) + Sum(case when LOCATION between 5.99 and 6.01 then 1 end)
)

What is the best way to duplicate rows based on multiple columns?

I have the following table below:
ID Start_Repeat_1 End_Repeat_1 Start_Repeat_2 End_Repeat_2
A 3 7 2 5
B 1 4 2 5
My goal is to duplicate "A" 5 times and "B" 4 times with the output below
ID Repeat_1 Repeat_2
A 3 2
A 4 3
A 5 4
A 6 5
A 7 NULL
B 1 2
B 2 3
B 3 4
B 4 5
The logic is that "A" needs to be duplicated with numbers between 3 and 7 in one column and numbers between 2 and 5 in another column so it needs to be duplicated at least 5 times.
The version below would also do. The order between the two columns does not matter.
ID Repeat_1 Repeat_2
A 3 2
A 7 5
A 6 3
A 5 NULL
A 4 4
Can someone help me with this using SQL Server 2018?
The dataset size is about 10,000 rows and each row is duplicated at most 10 times with a total of 10 columns like this
ID Repeat_1 Repeat_2 Repeat_3 Repeat_4 Repeat_10
A 3 2 1 1 1
B 7 5 1 1 1
You need to make use of a tally table. In the following solution, I have use a recursive cte to generate one.
Alternatively you can use a recursive cte to generate one on the fly
-- Tally Table
create table tally
(
n int
)
-- generate 1000 numbers for tally table
with cte as
(
select n = 0
union all
select n = n + 1
from cte
where n < 1000
)
insert into tally (n)
select n
from cte
The cross apply is to find the maximum difference between the repeat_1 or 2 etc.
-- The query
select t.ID,
Repeat_1 = case when n.n <= (End_Repeat_1 - Start_Repeat_1)
then t.Start_Repeat_1 + n.n
end,
Repeat_2 = case when n.n <= (End_Repeat_2 - Start_Repeat_2)
then t.Start_Repeat_2 + n.n
end,
Repeat_3 = case when n.n <= (End_Repeat_3 - Start_Repeat_3)
then t.Start_Repeat_3 + n.n
end
from tbl t
cross apply
(
select m = max(d)
from (
values
(End_Repeat_1 - Start_Repeat_1 + 1),
(End_Repeat_2 - Start_Repeat_2 + 1),
(End_Repeat_3 - Start_Repeat_3 + 1)
) n (d)
) m
inner join tally n on n.n >= 0
and n.n < m.m
dbfiddel demo
you can use recursive cte :
with cte as (
select * from test
union all
select Id
, case when Start_Repeat_1 + 1 > End_Repeat_1 then null else Start_Repeat_1 + 1 end
,End_Repeat_1
,case when Start_Repeat_2+ 1 > End_Repeat_2 then null else Start_Repeat_2+ 1 end
,End_Repeat_2
from cte
where Start_Repeat_1 <= End_Repeat_1 and Start_Repeat_2 <= End_Repeat_2
)
select ID,Start_Repeat_1,Start_Repeat_2
from cte
where coalesce(Start_Repeat_1,Start_Repeat_2) is not null
order by ID
ID | Start_Repeat_1 | Start_Repeat_2
:- | -------------: | -------------:
A | 3 | 2
A | 4 | 3
A | 5 | 4
A | 6 | 5
A | 7 | null
B | 1 | 2
B | 2 | 3
B | 3 | 4
B | 4 | 5
db<>fiddle here

SQL Adding Values based on the Hierarchy from Top to Bottom, with the bottom summing every from the top

The concept of what I am doing is summing the total waiting time from a higher level unto a lower level. And get the total waiting time for that particular entity. Below is a picture of the Levels of the Hierarchy:
My final output should be something like this.
The Total Waiting Time is explained by this instance.
Total Waiting Times:
A = 1
B = 1 + 2 = 3
C = 1 + 3 = 4
D = 1 + 2 + 4 = 7
E = 1 + 2 + 5 = 8
F = 1 + 3 + 6 = 10
G = 1 + 3 + 7 = 10
I have an idea of joining the table with itself, but somehow it does not work, but somehow it doesn't show the expected results. Is there a better way to do this? Thank you! :(
you can use recursive cte as shown below:
WITH cte AS (
SELECT *, waitingTime AS TotalWaitingTime
FROM waitingTime
UNION ALL
SELECT w.*, TotalWaitingTime + w.waitingTime
FROM waitingTime w
JOIN cte ON w.ParentObject = cte.Object
)
SELECT
object,
ParentObject,
waitingTime,
MAX(TotalWaitingTime) TotalWaitingTime
FROM cte
GROUP BY object,ParentObject,waitingTime
GO
object
ParentObject
waitingTime
TotalWaitingTime
A
1
1
B
A
2
3
C
A
3
4
D
B
4
7
E
B
5
8
F
C
6
10
G
C
7
11
db<>fiddle here

Check succeeding records for special constraint

A mathematical rule must apply to all records with the same name identifier:
x(n) = x(n-1) + y(n-1) where n are the elements sorted by x.
A special case: if y=0, the following value of x does not need to stick to this rule. As an example:
Name X Y
a 0 1
a 1 2
a 2 5 <----- this is invalid because 1 + 2 != 2
b 0 1
b 1 0
b 14 3 <----- this is okay because the preceding element had y = 0
b 16 1 <----- this is invalid because 14 + 3 != 16
The task is to filter the invalid elements.
Without the special case of y=0 I came up with this:
SELECT * FROM TABLE EXCEPT
SELECT NAME, X, Y FROM (
SELECT * FROM TABLE JOIN SELECT NAME AS N, X AS XX, Y AS YY
ON NAME = N WHERE X = 0 OR XX+YY = X)
Does anyone have any suggestion how to handle Y=0?
I have found a solution to your problem. I made an assumption that your table is named 'a'
Here is the code:
SELECT name,
x,
y
FROM
(SELECT a1.name,
a1.x,
a1.y,
(SELECT a3.x + a3.y
FROM a AS a3
WHERE a3.name=a1.name
AND a3.y<>0
AND a3.x =
(SELECT max(a2.x)
FROM a AS a2
WHERE a2.name=a1.name
AND a2.x<a1.x)) AS s1
FROM a AS a1)
WHERE s1 IS NULL
OR x=s1
and the result is:
a 0 1
a 1 2
b 0 1
b 1 0
b 14 3

oracle sql query question(grouping by 2 columns)

I have a table called testgroup in my database, which is like following:
I J
---------------------- ----------------------
1 a
1 a
2 a
1 b
1 c
2 b
3 d
2 b
2 b
3 d
Now, I want the result as below:
I J COUNT(J) in I
---------------------- ---------------------- ----------------------
1 a 2
2 a 1
1 b 1
1 c 1
2 b 3
3 d 2
...where count(j) in I is the number of each J related to the I.
For example: with I = 1, there are 2 a in column J, so the third column would be equal 2.
select I, J, count(*) as JinI
FROM atable
GROUP BY I, J
In fact the question is about counting I and J pairs:
select I, J, count(*) from tblName group by I, J