Selecting values based on value in another column with one being NULL - sql-server

I have the following table setup
Acc Currency Alias
1 NULL A
1 USD B
1 EUR C
I want to extract the alias by giving the input as Acc. The Currency and Acc are joined to another table.
It should work as follows -
If acc = 1 and currency is USD then B, if EUR then C, if null then A
Can someone help with this please ?

Did you actually try to write any SQL yourself?
SELECT ...
FROM otherTable ot
(INNER) JOIN thisTable tt
ON ot.Acc = tt.Acc AND ot.Currency = tt.Currency
...

Matching on NULL explicitly
SELECT *
FROM othertbl o
JOIN thistbl t on o.acc = t.acc
and (o.currency = t.currency or o.currency is null AND t.currency is null)
Or in your case, using the NULL currency from thistbl as a fallback
SELECT ...
FROM othertbl o
JOIN thistbl t on o.acc = t.acc and o.currency = t.currency
UNION ALL
SELECT ...
FROM othertbl o
JOIN thistbl t on o.acc = t.acc and t.currency IS NULL
WHERE NOT EXISTS (select *
from thistbl t2
where o.acc = t2.acc and o.currency = t2.currency)

Related

Select x value within a text - string_to_array

I have a column in table1 that contains names separated with commas, like a,b,c
names
result
a,d,e
a,c,e,f
c,d,f,g
Another column with a single name in table2, like a or b or c
line
name
origin
1
a
US
2
b
UK
3
c
UK
4
d
AUS
5
e
CAN
6
f
UK
7
g
UK
And I want to update table1.result if any names from table1.names are in table2.name & origin = UK.
Tried this, but getting error;
update table1 as t1
set result =
(select name from table2 where origin='UK') = any(string_to_array(t1.names, ','))
Use exists(...) if the result you want is boolean:
update table1 as t1
set result = exists(
select name
from table2
where origin = 'UK'
and name = any(string_to_array(t1.names, ','))
);
Test it in db<>fiddle.
If you want to get the names, use string_agg():
update table1 as t1
set result = (
select string_agg(name, ',')
from table2
where origin = 'UK'
and name = any(string_to_array(t1.names, ','))
);
Db<>fiddle.

SQL Server - coalesce data on duplicate keys when MERGE-ing

I've stumbled across an annoying situation where my source query results have duplicate keys with differing data. Unfortunately I need to back-fill any NULLs.
I tried with a MERGE but I get a key error.
The equivalent query in MySQL (that I cannot convert) is:
Please note that I have changed all the field and table names
INSERT INTO user_brief (name, high_score, colour)
SELECT
u.name,
h.high_score,
p.colour,
FROM foo_table AS f
LEFT JOIN users AS u ON f.user_id = u.id
LEFT JOIN high_scores AS h ON f.user_id = h.id
LEFT JOIN preferences AS p ON f.user_id = p.id
ON DUPLICATE KEY
UPDATE
name = COALESCE(user_brief.name, VALUES(name)),
high_score = COALESCE(user_brief.high_score, VALUES(high_score)),
colour = COALESCE(user_brief.colour, VALUES(colour));
SELECT Query Results
If we take just the SELECT you would get the following results:
name | high_score | color
---------------------------
foo | NULL | brown
foo | 40 | NULL
bar | 29 | blue
...
Desired Results
name | high_score | color
---------------------------
foo | 40 | brown
bar | 29 | blue
...
As you can see it has flattened (not sure if that's the correct term) taking the first non-null value for each column of a name keyed record.
My attempted MERGE solution (but it gets key errors):
MERGE INTO user_brief AS target
USING (SELECT
u.name,
h.high_score,
p.colour,
FROM foo_table AS f
LEFT JOIN users AS u ON f.user_id = u.id
LEFT JOIN high_scores AS h ON f.user_id = h.id
LEFT JOIN preferences AS p ON f.user_id = p.id) AS source
ON target.name = source.name
WHEN MATCHED THEN
UPDATE SET
target.name = COALESCE(source.name, target.name),
target.high_score = COALESCE(source.high_score, target.high_score),
target.colour = COALESCE(source.colour, target.colour)
WHEN NOT MATCHED BY TARGET THEN
INSERT (name, high_score, colour)
VALUES (source.name, source.high_score, source.colour);
You could use GROUP BY to flatten source:
WITH source AS (
SELECT
u.name,
high_score = MIN(h.high_score),
colour = MIN(p.colour)
FROM foo_table AS f
LEFT JOIN users AS u ON f.user_id = u.id
LEFT JOIN high_scores AS h ON f.user_id = h.id
LEFT JOIN preferences AS p ON f.user_id = p.id
GROUP BY u.name
)
MERGE INTO user_brief AS target
USING source
ON target.name = source.name
WHEN MATCHED THEN
UPDATE SET
target.name = COALESCE(source.name, target.name),
target.high_score = COALESCE(source.high_score, target.high_score),
target.colour = COALESCE(source.colour, target.colour)
WHEN NOT MATCHED BY TARGET THEN
INSERT (name, high_score, colour)
VALUES (source.name, source.high_score, source.colour);

T-SQL query to show all the past steps, active and future steps

I have 3 tables in SQL Server:
map_table: (workflow map path)
stepId step_name
----------------
1 A
2 B
3 C
4 D
5 E
history_table:
stepId timestamp author
----------------------------
1 9:00am John
2 9:20am Mary
current_stageTable:
Id currentStageId waitingFor
------------------------------------
12345 3 Kat
I would like to write a query to show the map with the workflow status. Like this result here:
step name time author
----------------------------
1 A 9:00am John
2 B 9:20am Mary
3 C waiting Kat
4 D
5 E
I tried left join
select
m.stepId, m.step_name, h.timestamp, h.author
from
map_table m
left join
history_table h on m.stepId = h.stepId
I thought it will list all the records from the map table, since I am using left join, but somehow it only shows 3 records which is from history table..
So I changed to
select
m.stepId, m.step_name, h.timestamp, h.author
from
map_table m
left join
history_table h on m.stepId = h.stepId
union
select
m.stepId, m.step_name, '' as timestamp, '' as author
from
map_table m
where
m.stageId not in (select stageId from history_table)
order by
m.stepId
Then it list the result almost as I expected, but how do I add the 3rd table in to show the current active stage?
Thank you very much for all your help!! Much appreciated.
Looks like it's what you asked:
with map_table as (
select * from (values (1,'A')
,(2,'B')
,(3,'C')
,(4,'D')
,(5,'E')) t(stepId, step_name)
)
, history_table as (
select * from (values
(1,'9:00am','John')
,(2,'9:20am','Mary')) t(stepId, timestamp, author)
)
, current_stapeTable as (
select * from (values (2345, 3, 'Kat')) t(Id, currentStageId, waitingFor)
)
select
m.stepId, m.step_name
, time = coalesce(h.timestamp, case when c.waitingFor is not null then 'waiting' end)
, author = coalesce(h.author, c.waitingFor)
from
map_table m
left join history_table h on m.stepId = h.stepId
left join current_stapeTable c on m.stepId = c.currentStageId
I think a union fits well with the data and avoids the coalescing the values on multiple joins.
with timeline as (
select stepId, "timestamp" as ts, author from history_table
union all
select currentStageId, 'waiting', waitingFor from current_stageTable
)
select step_id, step_name, "timestamp", author
from
map_table as m left outer join timeline as t
on t.stepId = m.stepId

SQL - Converting static pivot into dynamic pivot

I have the following static pivot that works:
SELECT *
FROM
(
SELECT
d.CODE,
a.ANAPARA - Isnull(b.ANAPARA,0) AS ANAPARA,
d.NAME_,
c.DUEDATE,
a.TAKSIT - Isnull(b.TAKSIT,0) AS TAKSIT,
e.CURCODE
FROM
(SELECT
PARENTREF,
SUM(TOTAL) AS ANAPARA,
SUM(INTTOTAL) AS FAIZ,
SUM(BSMVTOTAL) AS BSMV,
SUM(KKDFTOTAL) AS KKDF,
SUM(TOTAL+INTTOTAL+BSMVTOTAL+KKDFTOTAL) AS TAKSIT
FROM LG_011_BNCREPAYTR
WHERE TRANSTYPE = 0
GROUP BY PARENTREF) a
LEFT OUTER JOIN (SELECT
PARENTREF,
SUM(TOTAL) AS ANAPARA,
SUM(INTTOTAL) AS FAIZ,
SUM(BSMVTOTAL) AS BSMV,
SUM(KKDFTOTAL) AS KKDF,
SUM(TOTAL+INTTOTAL+BSMVTOTAL+KKDFTOTAL) AS TAKSIT
FROM LG_011_BNCREPAYTR
WHERE TRANSTYPE = 1
GROUP BY PARENTREF) b
ON a.PARENTREF = b.PARENTREF
INNER JOIN (SELECT
PARENTREF,
CREDITREF,
DUEDATE,
OPRDATE
FROM LG_011_BNCREPAYTR) c
ON a.PARENTREF=c.PARENTREF
INNER JOIN LG_011_BNCREDITCARD d
ON c.CREDITREF=d.LOGICALREF
INNER JOIN L_CURRENCYLIST e
ON d.TRCURR=e.CURTYPE OR (d.TRCURR=0 AND e.CURTYPE=160)
WHERE e.FIRMNR=11
) x
PIVOT
(
SUM(TAKSIT)
FOR CURCODE IN ([USD],[EUR],[YEN])
) xx
It creates the following table:
CODE ANAPARA NAME_ DUEDATE EUR USD YEN
001 103.35 CAR LOAN 2015-01-01 NULL 150.01 NULL
002 106.89 CONSUMER LOAN 2015-03-10 190.35 NULL NULL
003 110.44 CAR LOAN 2015-04-20 NULL NULL 200.51
004 103.22 CAR LOAN 2015-05-04 150.02 NULL NULL
However, I would like to convert it into a dynamic pivot. Hence, I would like to change the code so that it gets the distinct currency codes from e.CURCODE and produces the same pivot table.
THANKS A LOT.

To find percentage compliance using T-SQL

I'm not an expert in T-SQL so here I'm trying to find the % compliance for flu vaccine ,TB test and resiprator test by supervisor for medical staffs. Each employee has a supervisor name linked to their employee info. The below code works fine and it's giving me the % for the above tests. The problem is that I want to get the ID, Name and Department by Supervisor and the % compliance.
The expected output is like this:
Supervisor ID NAME Dept %Flu %TB %FIT
Elaine Jong 98% 100% 52%
001 MARY SURGERY
002 SUSAN SURGERY
James Ande 100% 98% 78%
267 JIM INPATIENT
789 SAM INPATIENT
Current OUTPUT
%Flu %TB %FIT
Elaine Jong 98% 100% 52%
James Ande 100% 98% 78%
And the Query:
SELECT E.FLDSUPRNAME AS Supervisor,
1.0*SUM(
CASE WHEN I.FLDDATE IS NULL
THEN 0 ELSE 1
END)/SUM(1) AS Percent_Flu_Compliant,
1.0*SUM(
CASE WHEN F.FLDDATE IS NULL OR (F.FLDDATE+365) < GETDATE()
THEN 0 ELSE 1
END) / SUM(1)
AS Percent_Fit_Compliant,
1.0*SUM(
CASE WHEN PPDx.FLDDATEDUE IS NULL
AND TBSSx.FLDDATEDUE IS NULL
AND CDUEx.FLDDATEDUE IS NULL
THEN 1 ELSE 0
END) /SUM(1) AS Percent_TB_Compliant
FROM EMPLOYEE E
LEFT OUTER JOIN DEPT D
ON D.FLDCODE= E.FLDDEPT
LEFT OUTER JOIN IMMUNE I ON I.FLDEMPLOYEE = E.FLDREC_NUM AND I.FLDTYPE IN ('109', '111')
AND I.FLDDATE = ( SELECT MAX(FLDDATE) FROM IMMUNE I2 WHERE E.FLDREC_NUM = I2.FLDEMPLOYEE
AND I2.FLDTYPE IN ('109','111') ) AND I.FLDDATE >= #Flu_Date AND I.FLDDATE <= GETDATE()
LEFT OUTER JOIN FITTEST F ON E.FLDREC_NUM = F.FLDEMPLOYEE
AND F.FLDDATE = (SELECT MAX(FLDDATE) FROM FITTEST F2 WHERE E.FLDREC_NUM = F2.FLDEMPLOYEE)
LEFT OUTER JOIN REQEXAM PPDx
ON PPDx.FLDEMPLOYEE = E.FLDREC_NUM
AND PPDx.FLDPHYSICAL = '110' AND
PPDx.FLDDATEDUE <= getdate()
LEFT OUTER JOIN REQEXAM PPDL
ON PPDL.FLDEMPLOYEE = E.FLDREC_NUM
AND PPDL.FLDPHYSICAL = '110'
LEFT OUTER JOIN REQEXAM TBSSx
ON TBSSx.FLDEMPLOYEE = E.FLDREC_NUM
AND TBSSx.FLDPHYSICAL = 'TBSS' AND
TBSSx.FLDDATEDUE <= getdate()
LEFT OUTER JOIN REQEXAM TBSSL
ON TBSSL.FLDEMPLOYEE = E.FLDREC_NUM
AND TBSSL.FLDPHYSICAL = 'TBSS'
LEFT OUTER JOIN REQEXAM CDUEx
ON CDUEx.FLDEMPLOYEE = E.FLDREC_NUM
AND CDUEx.FLDPHYSICAL = '109' AND
CDUEx.FLDDATEDUE <= getdate()
LEFT OUTER JOIN EMP S
ON S.FLDREC_NUM = E.FLDREC_NUM
WHERE E.FLDCOMP = #company
AND E.FLDSTATUS = 'A'
AND E.FLDSUPRNAME <> ' '
AND E.FLDID <> ' '
GROUP BY E.FLDSUPRNAME
ORDER BY E.FLDSUPRNAME
If I add ID,NAME and Dept on select and group by , SUM(1) will turn to 1 or 0, so I'm getting either 100% or 0% for all supervisors.
Any help on this is really appreciated.
thanks for your time.
USE an UNION, add blank columns to your first query and remove the order by:
SELECT (CASE WHEN ID IS NULL THEN Supervisor ELSE '' END) ,ID, name,dept,Percent_Flu_Compliant,Percent_TB_Compliant,Percent_Fit_Compliant FROM
(
SELECT E.FLDSUPRNAME AS Supervisor, NULL as ID, NULL as name, NULL as dept
(...)
GROUP BY hiddensupervisor, Supervisor, ID, name, dept
UNION ALL
SELECT E.FLDSUPRNAME Supervisor, E.id, E.name, E.dept, NULL as Percent_Flu_Compliant, NULL as Percent_TB_Compliant, NULL asPercent_Fit_Compliant
FROM Employee
) as q
ORDER BY supervisor, (CASE WHEN ID IS NULL THEN 1 ELSE 0 END),ID
we add the hidden supervisor column to be able to fit employees under their supervisor but leave that field blank there (we also could not add it and use case in the outer query, dunno which one would be faster). Apparently we have to try with case

Resources