SQL nested loops - sql

I have a problem, I have added a column to one of my database tables and now i need to populate this column with data.
Here are the tables:
Table QUESTIONNAIRE_QUESTIONS
with columns:
ID,
QUESTION_NUMBER,
PARENT_QUESTION_ID,
PARENT_QUESTIONNAIRE_ID,
QUESTION_CODE
Table QUESTIONNAIRE
with columns:
ID,
INTRODUCTION
What I first need to do is to find all unique QUESTIONNAIRE.ID and for each of those I need to find all QUESTIONNAIRE_QUESTIONS.ID that have this id as PARENT_QUESTIONNAIRE_ID and set the QUESTION_CODE as QUESTION_CODE=QUESTION_NUMBER*100.
Then, before moving on to the next QUESTIONNAIRE_ID i need to find all QUESTIONNAIRE_QUESTIONS.ID that have the current QUESTIONNAIRE_QUESTIONS.ID as PARENT_QUESTION_ID and set the QUESTION_CODE as
QUESTION_CODE=(parents question_code)+'.'+QUESTION_CODE.QUESTION_NUMBER*100.
How to perform this, complicated, nested loop? Seems like I have to use several loops?
Sample Data:
QUESTIONNAIRES
ID
1869359
1876176
QUESTIONNAIRE_QUESTIONS
ID QUESTION_NUMBER PARENT_QUESTIONNAIRE_ID PARENT_QUESTION_ID QUESTION_CODE
1869360 1 1869359 null null
1869362 2 1869359 null null
1869364 3 1869359 null null
1869367 1 null 1869364 null
1869369 1 1876176 null null
1869371 2 1876176 null null
1869372 3 1876176 null null
1869374 4 1876176 null null
1869377 1 null 1869372 null
And after i want it to look like this:
QUESTIONNAIRE_QUESTIONS
ID QUESTION_NUMBER PARENT_QUESTIONNAIRE_ID PARENT_QUESTION_ID QUESTION_CODE
1869360 1 1869359 null 100
1869362 2 1869359 null 200
1869364 3 1869359 null 300
1869367 1 null 1869364 300.100
1869368 1 null 1869367 300.100.100
1869369 2 null 1869367 300.100.200
1869370 1 1876176 null 100
1869371 2 1876176 null 200
1869372 3 1876176 null 300
1869374 4 1876176 null 400
1869377 1 null 1869371 200.100
Note that it can be more levels of "child questions"
Thanks!

UPDATE QUESTIONNAIRE_QUESTIONS
SET QUESTION_CODE = (QUESTION_NUMBER * 100)
WHERE QUESTIONNAIRE_QUESTIONS.ID IN (SELECT DISTINCT QUESTIONNAIRE.ID
FROM QUESTIONNAIRE, QUESTIONNAIRE_QUESTIONS
WHERE QUESTIONNAIRE_QUESTIONS.ID = PARENT_QUESTION_ID)
Not so sure about the 2nd question. Is that a typo?QUESTION_CODE.QUESTION_NUMBER*100

Related

INSERT a RANK into a SQL table

I have a table that has the following columns:
NAME (VARCHAR)
FISHING (INT)
SAILING (INT)
NAVIGATION (INT)
SALARY (NUMERIC)
This table has 9 rows of data. I used ALTER to add a column that would hold the rank of the person's salary. It is called SALARY_RANK. My idea was to populate that column with a SELECT statement that would fill that rank. I tried to use INSERT with the below SELECT statement to fill that column but it ends up creating 9 new rows where all except the new column are NULL.
What is the best way to go about this?
This is the SQL I have written for the new column:
ALTER TABLE #CODY_CREW
ADD SALARY_RANK INT;
INSERT INTO #CODY_CREW (SALARY_RANK)
SELECT
DENSE_RANK() OVER (ORDER BY SALARY) AS 'SALARY_RANK'
FROM #CODY_CREW
This is what happens when I run that:
NAME FISHING SAILING NAVIGATION SALARY SALARY_RANK
-------------------------------------------------- ----------- ----------- ----------- --------------------------------------- -----------
Amy 3 5 1 46000 NULL
Bill 1 2 5 43000 NULL
Carl 3 4 2 47000 NULL
Dan 4 3 1 36000 NULL
Eva 4 2 2 43000 NULL
Fred 1 3 4 55000 NULL
Greg 3 1 5 68000 NULL
Henry 5 4 2 64000 NULL
Ida 3 3 3 60000 NULL
NULL NULL NULL NULL NULL 1
NULL NULL NULL NULL NULL 2
NULL NULL NULL NULL NULL 2
NULL NULL NULL NULL NULL 3
NULL NULL NULL NULL NULL 4
NULL NULL NULL NULL NULL 5
NULL NULL NULL NULL NULL 6
NULL NULL NULL NULL NULL 7
NULL NULL NULL NULL NULL 8
(18 rows affected)
You should be using UPDATE instead of INSERT query. Try like below.
UPDATE C1
SET C1.SALARY_RANK = C2.SALARY_RANK
FROM #CODY_CREW C1
JOIN (SELECT
DENSE_RANK() OVER (ORDER BY SALARY) AS 'SALARY_RANK', Name
FROM #CODY_CREW) C2
ON C1.Name = C2.Name
In SQL Server, you can use an updatable CTE or subquery:
WITH toupdate AS (
SELECT cc.*, DENSE_RANK() OVER (ORDER BY SALARY) AS NEW_SALARY_RANK
FROM #CODY_CREW cc
)
UPDATE toupdate
SET SALARY_RANK = NEW_SALARY_RANK;
A JOIN is not necessary.

How to remove duplicate records in SQL table based on certain conditions and multi-criteria?

The following table consists of the columns EmployeeID, JobNum, CompDate.
Basically there are 3 different employees that have certain job ids and their completed date time associated with them. There are some jobNum that have no association to a particular EmployeeID and may have a complete date.
Problem:
1) Remove the records for a EmployeeID when the Complete date is not null or is populated with date.
2) Delete the record that has null values for both columns JobNum and CompDate for an Employee WHEN there is a record for that EmployeeID that consists of an open job (when JobNum is NOT NULL and CompDate is NULL). THIS IS FOR DUPLICATES.
Tried using ranking function with case statements. Does not rank properly.
[JobNum],
[CompDate],
RANK ( ) OVER( PARTITION BY [EmployeeID] ORDER BY
CASE WHEN ([JobNum] is null AND [CompDate] is null) THEN 1
WHEN ([JobNum] is not null AND [CompDate] is null) THEN 2
WHEN ([JobNum] is not null AND [CompDate] is not null) THEN 3
END ASC) as Rank
FROM [dbo].test1
WHERE [EmployeeID] IN (SELECT [EmployeeID] FROM dbo.test1
GROUP BY [EmployeeID]
HAVING COUNT(*) > 1)
EmployeeID JobNum CompDate Rank
1 NULL NULL 1
1 401 NULL 2
1 435 NULL 2
1 358 2019-07-15 15:10:57.810 4
2 285 NULL 1
2 299 2019-07-15 15:14:04.603 2
2 305 2019-07-14 15:10:57.810 2
2 330 2019-06-13 10:10:30.710 2
3 NULL NULL 1
3 435 NULL 2
3 402 2019-07-11 13:10:47.610 3
Ex:
EmployeeID JobNum CompDate Rank
Delete this -> 1 NULL NULL 1
when this exists -> 1 401 NULL 2
when this exists -> 1 435 NULL 2
1 358 2019-07-15 15:10:57.810 4
You seem to want only rows where compdate is null and one of the following two conditions:
jobnum is null
jobnum is not null and no rows for the employee have jobnum as null
I'm not sure what rank() has to do with these filtering conditions:
select t.*
from test1 t
where t.compdate is not null and -- condition 1
(t.jobnum is null or
not exists (select 1
from test1 tt
where tt.employeeid = t.employeeid and
tt.compdate is null and
tt.jobnum is null
)
);

Compare the column values where there are null and not null values

I want to compare the values from a table where there are null and not null values for the same ID ( If not null then I want minimum of two not null values). IF all the values are null for the given ID, I want the values to be displayed as null for the ID.
Input
ID Amount
1 Null
1 Null
1 Null
1 500
1 600
1 700
2 Null
2 Null
2 Null
2 Null
2 Null
3 Null
3 Null
3 300
3 600
3 200
Expected output
ID Amount
1 500 (min Not null value)
2 Null
3 200 (Min Not null value)
Simple grouping will do the trick:
select t.id, min(t.amount)
from table t
group by t.id

SQL merge and sum multiple rows into one

Essentially I have a SQL table that looks like this;
Name Week 1 Week 2 Week 3 Week 4 Week 5
James NULL 5 NULL NULL NULL
James 10 NULL NULL NULL NULL
James NULL NULL NULL 5 NULL
James NULL NULL NULL 20 NULL
Tom NULL NULL 5 NULL NULL
Tom NULL 10 NULL NULL NULL
Tom 25 NULL NULL NULL NULL
Tom NULL NULL NULL 5 NULL
Tom NULL NULL NULL 5 NULL
And I would want to combine together the rows (while also summing the values) to get something more like this;
Name Week 1 Week 2 Week 3 Week 4 Week 5
James 10 5 20 25 NULL
Tom 25 10 5 10 NULL
But I can't think of a good way of doing so. My data has quite the number of columns and rows so I'm looking for something which ideally doesn't require listing out all the individual column or row names.
Thanks
Just Sum the Rows and Group by name
select
Name
, Week1 = sum(Week 1)
, Week2 = sum(Week 2)
, Week3 = sum(Week 3)
, Week4 = sum(Week 4)
, Week5 = sum(Week 5)
from Table
group by Name

lookup tables with temporary fields?

I have a query that produces the following results:
table1:
degree_code occupation_code degree_completions degree_level
1.0000 20-2021 10 1
1.0000 20-2022 10 1
1.1051 52-2095 2 3
1.1051 52-2095 41 2
1.5010 15-1100 2 3
I have another small, lookup table that relates degree_level to custom categories that I need to use:
table2
degree_level degree_level_recode
1 AADEGREE
2 AADEGREE
3 BACHDEGREE
I would like to build the output of of the first query to report the following format:
degree_code occupation_code degree_completions degree_level AADEGREE BACHDEGREE
1.0000 20-2021 10 1 10 0
1.0000 20-2022 10 1 10 0
1.1051 52-2095 2 3 3 0
1.1051 52-2095 41 2 0 41
1.5010 15-1100 2 3 2 1
Basically, create new temporary recode fields in the original query that report the degree_completions underneath them when they match the degree_level_recode, input 0 if no match. This is highly simplified; I will be performing operations on the recode fields against other fields in each element in the query.
I've shown the degree_completions field for reference, but I'd leave it out in the final query for obvious redundancy reasons.
I'm thinking I need to use a CASE statement or something similar for comparison checking, but I'm new to SQL.
EDIT:
In reference to Cha's answer below, take a revised table1 output (after mapping the recode):
degree_code degree_level degree_completions degree_level_recode
01.0601 2 11 LESSCOLL
01.0601 3 22 AADEGR
01.0605 2 3 LESSCOLL
Consider this code (table2 is referenced above the edit):
SELECT degree_code
,degree_level
,[LESSCOL] AS LESSCOL
,[AADEGR] AS AADEGR
,[BACHDEGR] AS BACHDEGR
,[MADEGR] AS MADEGR
,[DOCDEGR] AS DOCDEGR
FROM
(
SELECT degree_code
,table1.degree_level
,degree_level_recode
,degree_code
FROM table1
,table2
WHERE table1.degree_level = table2.degree_code
) AS p
PIVOT
(
SUM (degree_completions)
FOR degree_level_recode IN ([LESSCOL], [AADEGR], . . .)
) AS pvt
Which produces these results:
degree_code degree_level LESSCOL AADEGR BACHDEGR MADEGR DOCDEG
01.0601 2 NULL NULL NULL NULL NULL
01.0601 3 NULL 22 NULL NULL NULL
01.0505 2 NULL NULL NULL NULL NULL
I'm trying to get to:
degree_code degree_level LESSCOL AADEGR BACHDEGR MADEGR DOCDEG
01.0601 2 11 NULL NULL NULL NULL
01.0601 3 NULL 22 NULL NULL NULL
01.0505 2 3 NULL NULL NULL NULL
Additionally, replace NULL with 0.
This is what you are after (assumptions: your first table is called #temp1, your second table is called #temp2):
SELECT *
FROM
#temp1,
(SELECT degree_level, [AADEGREE] as col1, [BACHDEGREE] as col2
FROM
(SELECT degree_completions, #temp1.degree_level, degree_level_recode
FROM #temp1, #temp2 WHERE #temp1.degree_level = #temp2.degree_level) AS p
PIVOT
(
SUM (degree_completions)
FOR degree_level_recode IN
([AADEGREE], [BACHDEGREE])
) AS pvt
) as V
WHERE #temp1.degree_level = V.degree_level
ORDER BY 1