Using CASE WHEN across tables - sql

I have a few tables that look like
SQL> select * from stipend
2 order by subjectid, stipdate;
SUBJECTID STIPDATE AMOUNT
---------- ----------- ----------
10011 31-oct-2021 800
10111 31-jul-2019 2000
10111 31-jul-2021 1500
20022 31-jul-2020 1200
30033 29-feb-2020 1400
40044. 31-jul-2020 1200
40044 31-jul-2021 2000
50055 31-jul-2021 2000
50055 30-sep-2021 1000
SQL> select * from subject
2 order by subjectid;
SUBJECTID LNAME FNAME PROJID
---------- ------------ --------------------
10011 Indy Eva XYZ01
10111 Isner Monica XYZ04
11011 Dupont Marty XYZ05
20022 Jordan Sam XYZ01
30033 Jordan Mary XYZ01
40044 Belmont Renee XYZ02
50055 Pissaro Becky XYZ02
60066 Nadal Becky XYZ03
70077 Bardot Brigitte XYZ03
80088 null Eva XYZ03
90099 Garnet Larry XYZ04
And I want to update the stipends that are in Project XYZ01 and XYZ02 by 40% using the CASE WHEN construct. Obviously the two tables are connect by stipend.subjectid = subject.subjectid and the I would use the subject.projectid to determine this, but how would I do that with CASE WHEN?

Have you tried this?
SELECT
SUBJECTID
,PROJID
,CASE WHEN ProjectID in ('XYZ01','XYZ02') THEN AMOUNT * 1.4 ELSE AMOUNT END as AMOUNT
FROM subject sb
LEFT JOIN stipend st ON st.SUBJECTID = sb.SUBJECTID

You may try this. Here I have used Stipend table as LEFT Table, you may alter this:
SELECT
st.SUBJECTID, sb.PROJID, Amount,
CASE WHEN ProjID in ('XYZ01','XYZ02') THEN AMOUNT * 1.4
ELSE AMOUNT
END as Updated_AMOUNT
FROM stipend st
LEFT JOIN subject sb
ON st.SUBJECTID = sb.SUBJECTID
Note: Using subject as left table may result in different level. Try them in Demo link below.
Demo

Pls check this. I have no data, so haven't tested it.
MySQL:
UPDATE
stipend st
join subject sub on st.SUBJECTID = sub.SUBJECTID
SET
AMOUNT = CASE WHEN
sub.ProjectID in ('XYZ01', 'XYZ02')
THEN AMOUNT * 1.4
ELSE AMOUNT
END
Oracle
I guess SQL Plus has similar syntax of Oracle DB, you can try this
Note: This syntax is converted online by https://www.sqlines.com/online . Pls check it
UPDATE
stipend st
join subject sub on st.SUBJECTID = sub.SUBJECTID
AMOUNT := CASE WHEN
sub.ProjectID in ('XYZ01', 'XYZ02')
THEN AMOUNT * 1.4
ELSE AMOUNT
END

Better to go for a regular join as left join would pull duplicate values in your result.
UPDATE STPIEND SET AMOUNT = CASE WHEN T1.AMOUNT IN ('XYZ01','XYZ02') THEN T1.AMOUNT * 1.4
ELSE T1.AMOUNT
END AS UPDATED_AMOUNT
FROM STIPEND T1 JOIN SUBJECT T2 ON T1.SUBJETID = T2.SUBJETID

Related

Using CASE WHEN To Update

I have a few tables that look like
select * from stipend
order by subjectid, stipdate;
SUBJECTID STIPDATE AMOUNT
---------- ----------- ----------
10011 31-oct-2021 800
10111 31-jul-2019 2000
10111 31-jul-2021 1500
20022 31-jul-2020 1200
30033 29-feb-2020 1400
40044. 31-jul-2020 1200
40044 31-jul-2021 2000
50055 31-jul-2021 2000
50055 30-sep-2021 1000
select * from subject
order by subjectid;
SUBJECTID LNAME FNAME PROJID
---------- ------------ --------------------
10011 Indy Eva XYZ01
10111 Isner Monica XYZ04
11011 Dupont Marty XYZ05
20022 Jordan Sam XYZ01
30033 Jordan Mary XYZ01
40044 Belmont Renee XYZ02
50055 Pissaro Becky XYZ02
60066 Nadal Becky XYZ03
70077 Bardot Brigitte XYZ03
80088 null Eva XYZ03
90099 Garnet Larry XYZ04
And I want to update the stipends that are in Project XYZ01 and XYZ02 by 40% using the CASE WHEN construct. Obviously the two tables are connect by stipend.subjectid = subject.subjectid and the I would use the subject.projectid to determine this, but how would I do that with CASE WHEN?
This is how I did it before
update stipend
set amount = amount + (amount * .40)
where subjectid in
(select subjectid from subject
where subject.projid = 'XYZ01' or subject.projid = 'XYZ02');
and that worked, but now I need to do the same thing but with CASE WHEN.
Eddie, below is a solution that uses the CASE WHEN statement. But, as Mureinik mentioned on the comments, your solution is better than using a CASE WHEN statement in this case.
-- using CASE WHEN
update s
set s.amount = (case when x.projid in ('XYZ01','XYZ02')
then s.amount + (s.amount * .40)
else s.amount
end)
from stipend s
join subject x on x.subjectid = s.subjectid
Note: I did not add x.projid in ('XYZ01','XYZ02') to the where clause as that would defeat the purpose of using the CASE WHEN. So, this would run for all the rows.

Unable to make desired JOINS between multiple tables

I've got 3 tables
Plan:
plan_id emp_id duration
123 1010 30
456 1011 40
789 1012 60
PlanEmp:
plan_id emp_id
123 2131
456 3131
789 4131
Emp:
emp_id Name
1010 Andy
1011 Cole
1012 John
2131 Sam
3131 Kim
4131 Ray
Desired Summary Output:
plan_id Name duration
123 Andy 30
123 Sam 30
456 Cole 40
456 Kim 40
789 John 60
789 Ray 60
Query that I'm trying to modify to get the above result:
SELECT P.plan_id
,E.Name
,P.duration
FROM Plan P
LEFT JOIN Emp E
ON P.emp_id = E.emp_id
LEFT JOIN PlanEmp PE
ON P.plan_id = PE.plan_id
I'm unable to figure how to pull the Employee details using the PlanEmp table AND the Plan table to get the summary output.
This should work:
SELECT sub.plan_id, sub.emp_id, Emp.name, Plan.duration
FROM
(SELECT plan_id, emp_id
FROM Plan
UNION
SELECT plan_id, emp_id
FROM PlanEmp) sub
LEFT JOIN Emp
ON sub.emp_id = Emp.emp_id
LEFT JOIN Plan
ON sub.plan_id = Plan.plan_id
ORDER BY plan_id
Tested here: http://sqlfiddle.com/#!9/21ca79/4
Use the below script.
;With cte_1
As( select plan_id,emp_id
From plan
UNION
Select plan_id,emp_id
From plan_emp)
Select c.plan_id,e.Name,p.Duration
From cte_1 c
Join plan p on c.plan_id=p.plan_id
Join emp e on c. Emp_id=e.emp_id
I'm sure there's a logical explanation for this and since my reputation is not at the level to comment yet, but why is emp_id on both the Plan and PlanEmp tables? If you can normalize this, that would be a good idea to move emp_id off of the Plan table to the PlanEmp table.
Your table design is not following the needed Normalization practices. You should merge the table Plan and EmpPlan.
or
Your design should be (To normalize more):
Table: Plan
Plan_ID
Duration
Table: PlanEmp
Plan_ID
Emp_ID
Your existing structure can be queried with multiple ways but they may not efficient. Another way:
Select ISNULL(P.plan_id, PE.plan_id) Plan_ID, E.Name,
(Select Duration from #Plan pp Where pp.plan_id = ISNULL(P.plan_id, PE.plan_id)) as Duration
from Emp E
left Join #lan P on E.emp_id = P.emp_id
left Join PlanEmp PE on E.emp_id = PE.emp_id
Where P.emp_id IS NOT NULL or PE.emp_id is not null

Include Conditions to Sum for Two Fields in Group By Select Statement

I am pretty new to Oracle SQL (but have some basic SQL knowledge). Consider the following data structure:
Region Plan Code SM_Name Charge Commission
EMEA SM abc john smith DARK 10
Asia SM def bob jones MAKER 40
AmericasSM ghi chris eng TAKER 30
AmericasSM xyz bob marks FUND 40
Asia SM def brian bee DARK 10
I want to create a SELECT statement that will Sum the Commissions by Charge and place it in two separate columns.
So like this
Region Plan Code SM_Name DIRECT TRX
EMEA SM abc john smith 10
Asia SM def bob jones 40
AmericasSM ghi chris eng 30
AmericasSM xyz bob marks 40
Asia SM def brian bee 10
I am attempting to use CASE statements to create this from the research I have done, but it's not quite working. My SQL is below:
SELECT a.REGION, 'SM', a.TRADING_CODE, a.SM,
CASE
WHEN a.CHARGE = 'FUND' THEN SUM(a.COMMISSION)
END AS DIRECT,
CASE
WHEN a.CHARGE IN ('DARK', 'MAKER', 'TAKER') THEN SUM(a.COMMISSION)
END AS TRX
FROM WORK_COMMISSION_RESULTS a
GROUP BY a.REGION, 'SM', a.TRADING_CODE, a.SM;
I get the error "not a GROUP BY expression" when running the code. I understand that maybe IF statements may work as well, but I am a bit lost.
Can anyone assist in getting me in the right direction?
Use conditional aggregation:
select Region, Plan, Code, SM_Name,
sum(case when charge = 'FUND' then Commission end) as Direct,
sum(case when charge <> 'FUND' then Commission end) as TRX
from WORK_COMMISSION_RESULTS wcr
group by Region, Plan, Code, SM_Name ;
Move the SUM outside the CASE statement:
SELECT a.REGION, 'SM', a.TRADING_CODE, a.SM,
SUM(CASE
WHEN a.CHARGE = 'FUND' THEN a.COMMISSION
END) AS DIRECT,
SUM(CASE
WHEN a.CHARGE IN ('DARK', 'MAKER', 'TAKER') THEN a.COMMISSION
END) AS TRX
FROM WORK_COMMISSION_RESULTS a
GROUP BY a.REGION, 'SM', a.TRADING_CODE, a.SM;

SQL - Return number times a value results from a query

I'm fairly new to SQL server (this is my second post on StackOverflow). I'm currently using SQL Server 2008 R2. I have a few tables that I've joined with the following query:
SELECT pt.first_name, pt.last_name, pt.provider_id,
pt.last_visit_date, pl.last_name
FROM dbo.pm_patient pt
INNER JOIN dbo.ProviderList pl
ON pt.provider_id = pl.provider_id
WHERE pt.last_visit_date >= '02/01/2012' AND pt.last_visit_date <= '02/01/2013'
ORDER BY provider_id
The result of the above query looks something like this:
first_name last_name provider_id last_visit_date last_name
Smith John 1 04/25/2012 Johnson
Doe Jane 1 02/25/2012 Johnson
Davies Ann 1 03/15/2012 Johnson
Dupree David 1 11/20/2012 Johnson
Jones Becky 1 04/21/2012 Smith
Diaz Mike 1 02/12/2012 Smith
Williams Allison 1 08/05/2012 Smith
Taylor Joe 1 10/01/2012 Smith
I would rather simply get the following result:
last_name NoOfPatients
Johnson 4
Smith 4
Could someone please help me?
Try :
SELECT pl.last_name, count(*) NoOfPatients
FROM dbo.pm_patient pt
INNER JOIN dbo.ProviderList pl
ON pt.provider_id = pl.provider_id
WHERE pt.last_visit_date >= '02/01/2012' AND pt.last_visit_date <= '02/01/2013'
GROUP BY pl.last_name
You need to read about how to use group in querys, basically what you need to do is count the number of patients and group by providers.
SELECT pt.last_name, COUNT(pt.*) NoOfPatients
FROM dbo.pm_patient pt
INNER JOIN dbo.ProviderList pl
ON pt.provider_id = pl.provider_id
WHERE pt.last_visit_date >= '02/01/2012' AND pt.last_visit_date <= '02/01/2013'
ORDER BY provider_id
GROUP BY pt.last_name

SQL Join Ignore multiple matches (fuzzy results ok)

I don't even know what the name of my problem is called, so I'm just gonna put some sample data. I don't mind fuzzy results on this (this is the best way I can think to express it. I don't mind if I overlook some data, this is for approximated evaluation, not for detailed accounting, if that makes sense). But I do need every record in TABLE 1, and I would like to avoid the nulls case indicated below.
IS THIS POSSIBLE?
TABLE 1
acctnum sub fname lname phone
12345 1 john doe xxx-xxx-xxxx
12346 0 jane doe xxx-xxx-xxxx
12347 0 rob roy xxx-xxx-xxxx
12348 0 paul smith xxx-xxx-xxxx
TABLE 2
acctnum sub division
12345 1 EAST
12345 2 WEST
12345 3 NORTH
12346 1 TOP
12346 2 BOTTOM
12347 2 BALLOON
12348 1 NORTH
So if we do a "regular outer" join, we'd get some results like this, since the sub 0's don't match the second table:
TABLE AFTER JOIN
acctnum sub fname lname phone division
12345 1 john doe xxx-xxx-xxxx EAST
12346 0 jane doe xxx-xxx-xxxx null
12347 0 rob roy xxx-xxx-xxxx null
12348 0 paul smith xxx-xxx-xxxx null
But I would rather get
TABLE AFTER JOIN
acctnum sub fname lname phone division
12345 1 john doe xxx-xxx-xxxx EAST
12346 0 jane doe xxx-xxx-xxxx TOP
12347 0 rob roy xxx-xxx-xxxx BALLOON
12348 0 paul smith xxx-xxx-xxxx NORTH
And I'm trying to avoid:
TABLE AFTER JOIN
acctnum sub fname lname phone division
12345 1 john doe xxx-xxx-xxxx EAST
12345 1 john doe xxx-xxx-xxxx WEST
12345 1 john doe xxx-xxx-xxxx NORTH
12346 0 jane doe xxx-xxx-xxxx TOP
12346 0 jane doe xxx-xxx-xxxx BOTTOM
12347 0 rob roy xxx-xxx-xxxx BALOON
12348 0 paul smith xxx-xxx-xxxx NORTH
So I decided to go with using a union and two if conditions. I'll accept a null for conditions where the sub account is defined in table 1 but not in table 2, and for everything else, I'll just match against the min.
If I'm understanding correctly, it looks like you're trying to join on the sub column if it matches. If there's no match on sub, then you want it to select the "first" row for that acctnum. Is this correct?
If so, you'll need to left join on the full match, then perform another left join on a select statement that determines the division that corresponds to the lowest sub value for that acctnum. The row_number() function can help you with this, like this:
select
t1.acctnum,
t1.sub,
t1.fname,
t1.lname,
t1.phone,
isnull(t2_match.division, t2_first.division) as division
from table1 t1
left join table2 t2_match on t2_match.acctnum = t1.acctnum and t2_match.sub = t1.sub
left join
(
select
acctnum,
sub,
division,
row_number() over (partition by acctnum order by sub) as rownum
from table2
) t2_first on t2_first.acctnum = t1.acctnum
EDIT
If you don't care at all about which record you get back from table 2 when a matching sub doesn't exist, you could combine two different queries (one that matches the sub and one that just takes the min or max division) with a union.
select
t1.acctnum,
t1.sub,
t1.fname,
t1.lname,
t1.phone,
t2.division
from table1 t1
join table2 t2 on t2.acctnum = t1.acctnum and t2.sub = t1.sub
union
select
t1.acctnum,
t1.sub,
t1.fname,
t1.lname,
t1.phone,
min(t2.division)
from table1 t1
join table2 t2 on t2.acctnum = t1.acctnum
left join table2 t2_match on t2_match.acctnum = t1.acctnum and t2_match.sub = t1.sub
where t2_match.acctnum is null
Personally, I don't find the union syntax any more compelling and you now have to maintain the query in two places. For this reason, I'd favor the row_number() approach.
try to use
SELECT MIN(Table_1.acctnum) as acctnum , MIN(Table_1.sub) as sub,MIN( Table_1.fname) as fname, MIN(Table_1.lname) as name, MIN(Table_1.phone) as phone, MIN(Table_2.division) as division
FROM Table_1 INNER JOIN Table_2 ON Table_1.acctnum = Table_2.acctnum AND Table_1.sub = Table_2.sub
where Table_1.sub>0
group by Table_1.acctnum
union
SELECT MIN(Table_1.acctnum) as acctnum , MIN(Table_1.sub) as sub,MIN( Table_1.fname) as fname, MIN(Table_1.lname) as name, MIN(Table_1.phone) as phone, MIN(Table_2.division) as division
FROM Table_1 INNER JOIN Table_2 ON Table_1.acctnum = Table_2.acctnum
where Table_1.sub=0
group by Table_1.acctnum
this is the result
12345 1 john doe xxxxxxxxxx EAST
12346 0 jane doe xxxxxxxxxx BOTTOM
12347 0 rob roy xxxxxxxxxx BALLOON
12348 0 paul smith xxxxxxxxxx NORTH
if you change min to max TOP will be insted of BOTTOM on the second row
It may also work for you:
SELECT t1.acctnum, t1.sub, t1.fname, t1.lname, t1.phone,
ISNULL(MAX(t2.division),MAX(t3.division)) as division
FROM table_1 t1
LEFT JOIN table_2 t2 ON (t2.acctnum = t1.acctnum AND t1.sub = t2.sub)
LEFT JOIN table_2 t3 ON (t3.acctnum = t1.acctnum)
GROUP BY t1.acctnum, t1.sub, t1.fname, t1.lname, t1.phone
This will give your desired result, exactly (for the shown data):
Updated to not assume there is always a sub==1 value:
SELECT
T1.acctnum,
T1.sub,
T1.fname,
T1.lname,
T1.phone,
T2.division
FROM
TABLE_1 T1
LEFT JOIN
TABLE_2 T2 ON T1.acctnum = T2.acctnum
AND
T2.sub = (SELECT MIN(T3.sub) FROM TABLE_2 T3 WHERE T1.acctnum = T3.acctnum)
ORDER BY
T1.lname,
T1.fname,
T1.acctnum