lookup tables with temporary fields? - sql

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

Related

SQL Return rows with mix of nulls and non nulls in certain columns

If I have the following table
id a b c time
-----------------------------
0 1 4 "ca" 23
1 NULL NULL NULL 18
2 NULL 1 "pn" 13
3 6 NULL "ar" 27
4 1 2 NULL 24
I want to return all rows with at least one null and one non-null in columns a, b, and c. So I want to return:
id a b c time
-----------------------------
2 NULL 1 "pn" 13
3 6 NULL "ar" 27
4 1 2 NULL 24
I know I can write
select *
from table
where ((a is null and (b is not null or c is not null))
or (a is not null and (b is null or c is null)))
But what happens if I need to consider 4 columns or more? It becomes a mess. Note that the table could have 20 or more columns, of which I am only considering a small subset of columns for null/non-null analysis. Is there a concise way of doing this? Thanks
One method would be to unpivot your data, and COUNT the NULL and non-NULL values, and filter on that:
SELECT V.ID,
V.a,
V.b,
V.c,
V.time
FROM (VALUES(0,1,4,'"ca"',23),
(1,NULL,NULL,NULL,18),
(2,NULL,1,'"pn"',13),
(3,6,NULL,'"ar"',27),
(4,1,2,NULL,24))V(ID,a,b,c,time)
CROSS APPLY (SELECT COUNT(UP.V) AS NonNull,
COUNT(CASE WHEN UP.V IS NULL THEN 1 END) AS IsNull
FROM (VALUES(CONVERT(varchar(1),V.a)),
(CONVERT(varchar(1),V.b)),
(CONVERT(varchar(1),V.c)))UP(V))C
WHERE C.[IsNull] > 0
AND C.NonNull > 0;

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

How to update a field's value by an incremental value without using loop in SQL Server 2008?

I have a table #temp in SQL Server 2008 like this:
Id p_id h_no f_id
------------------
1 100 A01 null
2 200 A02 null
3 300 A02 null
4 400 null null
5 500 null null
6 600 A03 null
7 700 A01 null
8 400 null null
So basically, every record has a p_id, but may or may not have h_no.
What I want is to replace f_id values with a dummy incremental number based on:
if h_no value of a record matches another(s), this (those) ones will have same f_id (check ids:1 & 7 or ids:2 & 3 in the example)
if h_no is null but p_id values are equal for some cases, they will have same f_id (check ids: 4 & 8 in the example)
For example, the sample table above should be:
Id p_id h_no f_id
-----------------
1 100 A01 1
2 200 A02 2
3 300 A02 2
4 400 null 3
5 500 null 4
6 600 A03 5
7 700 A01 1
8 400 null 3
I do not want to use a loop for this process. I am trying to find a more optimal solution for this. I need a query something like below, could not find the correct syntax.
declare #tempFID int = 1;
update t
set t.f_id = #tempFID++ --syntax error
from #temp t
inner join #temp t2 on t.Id = t2.Id
where (t.h_no is not null and t.h_no = t2.h_no)
or (t.h_no is null and t.p_id = t2.p_id)
I also tried but had syntax error:
update t
set t.f_id = (set #tempFID = #tempFID + 1) --syntax error
...
Any help would be so appreciated!
;WITH cte AS (
SELECT *
,CASE WHEN h_no IS NULL THEN p_id ELSE MIN(p_id) OVER (PARTITION BY h_no) END as PIdGroup
FROM
#Table
)
, cteFIdValue AS (
SELECT
Id
,DENSE_RANK() OVER (ORDER BY PIdGroup) as f_id
FROM
cte
)
UPDATE t
SET f_id = u.f_id
FROM
Table t
INNER JOIN cteFIdValue u
ON t.ID = u.ID
Find the minimum p_id for each h_no and just leave it as the assigned p_id if h_no is null
Then create a dense rank on the PidGroup
Update the Table
so you have problems besides a syntax error in your code above. First your join will only get the exact same record, you would have to change to t.ID <> t2.ID as left join and still need some sort of ranking. honestly I am not positive what you are attempting there.
This approach might be simpler:
update #temp
set f_id = isnull(f_id, 0) +
case when condition1 is met then value 1
etc
when final condition is met then 0
else null
end

SQL left join - and after on clause is not working

I have a scenario in left join of SQL which is not generating required output which i need. Following is description in tabular form and my tried queries,
Table A
A_ID // PK OF TABLE A
IS_ACTIVE // VALUE=1 OR 0
Table B
B_ID // PK OF TABLE B
A_ID // FK OF TABLE A IN TABLE B
Sample Records of Table A
A_ID IS_ACTIVE
1 1
2 0
3 1
4 0
5 0
Sample Records of Table B
B_ID A_ID
1 1
2 1
3 4
4 4
5 4
6 4
Select * from A left join B on A.A_ID=B.A_ID
A_ID IS_ACTIVE B_ID A_ID
1 1 1 1
1 1 2 1
2 0 NULL NULL
3 1 NULL NULL
4 0 3 4
4 0 4 4
4 0 5 4
4 0 6 4
5 0 NULL NULL
Select * from A left join B on A.A_ID=B.A_ID and A.IS_ACTIVE=0
Following output is the actual output of above query with no effect to records by adding AND is_active=0 after ON clause.
A_ID IS_ACTIVE B_ID A_ID
1 1 1 1
1 1 2 1
2 0 NULL NULL
3 1 NULL NULL
4 0 3 4
4 0 4 4
4 0 5 4
4 0 6 4
5 0 NULL NULL
Following output is the required output which i need to solve my problem.
A_ID IS_ACTIVE B_ID A_ID
1 1 NULL NULL
1 1 NULL NULL
2 0 NULL NULL
3 1 NULL NULL
4 0 3 4
4 0 4 4
4 0 5 4
4 0 6 4
5 0 NULL NULL
I am facing problem in getting exact records which are required.
I need all records from Table A and matching records from Table B but
those records of Table B which are equal to is_active=0 of Table A.
Note : Query should show all records of Table A
Please help me how can i get this scenario in Left Join of SQL.
I tried your examples as code. And I get the result you needed. What is the problem?
CREATE TABLE #a(a_id int, is_active bit)
CREATE TABLE #b(b_id int, a_id int)
INSERT INTO #a(a_id,is_active)
VALUES(1,1),(2,0),(3,1),(4,0),(5,0)
INSERT INTO #b(b_id,a_id)
VALUES(1,1),(2,1),(3,4),(4,4),(5,4),(6,4)
SELECT *
FROM #a as a
LEFT JOIN #b as b
ON a.a_id = b.a_id
AND a.is_active = 0
DROP TABLE #a
DROP TABLE #b
Have you tried:
Select * from A left join B on A.A_ID=B.A_ID
Where A.IS_ACTIVE=0

Sorting in SQL w/ multiple conditions

I'm new to SQL, how would I go along with sorting the following:
I have three different type of dev projects(type 1,2, and 3). Type 1 and 2 also have an analysis that is linked with them. The analysis and dev type 1 and 2 will have a linking parent id, how would I sort with these specs:
Process Items in the following sequence
Dev type 3 projects
Dev type 1 or 2 projects combined with their analysis item (dev item columns take precedence analysis item columns)
Remaining analysis projects with no related dev projects
*EDIT* the lower ParentID should come first after these three specs
Example columns of the table are:
|ParentID| AnalysisItemID | DevItemId | DevType |
1 Null 2 1
4 5 Null Null
6 8 Null Null
8 Null 9 3
6 Null 7 2
1 3 Null Null
Sorted Output would be:
|ParentID| AnalysisItemID | DevItemId | DevType |
8 Null 9 3
1 Null 2 1
1 3 Null Null
6 Null 7 2
6 8 Null Null
4 5 Null Null
To me, it looks like you want to sort the data by the parent id, with the ordering based on the maximum devtype for the parent.
For this, you need to join to a summary table to get the information for the order by:
select t.*
from t join
(select parentId, max(devtype) as maxdt
from t
group by parentId
) p
on t.parentId = p.parentId
order by (case when maxdt = 3 then 1
when maxdt in (1, 2) then 2
else 3
end), parentId, devtype desc
It's ugly, but you can order by the result of a CASE in SQL. I imagine something similar to:
SELECT * FROM MyTable
ORDER BY CASE WHEN DevType = 3 THEN 1
WHEN DevType IN (1, 2) THEN AnalysisItemID + 1
ELSE [bignumber] + AnalysisItemID END
could do what you require.
Obviously, that case can also be encapsulated into a user-defined function for readability if required. There's a chance that that could affect performance, however.