Mysql query null field replace with custom if statement - mysql

I have the below Mysql query doest quite work as expected yet. I am not a Mysql expert but basically what I am trying to do is for every AptStatus = 3 I have a Null field for Operatory. Id like to convert that Null field to the Operatory name for each count of the location. (hopefully that makes sense).
select Location, AptDate, IF(AptStatus = 3, '08:00:00', AptTime) AptTime, IF(AptStatus = 3, 540, AptLength) AptLength,AptStatus, OperatoryNum, Operatory
from rpt_officeschedules
where AptTime between '07:59:00' and '17:30:00' and AptDate between '2020-11-07' and '2020-11-08' and (Operatory LIKE '%NP%' or Operatory LIKE '%OPEN%'or Operatory is null )
order by Location, AptDate, OperatoryNum,AptTime, Operatory;
Sample Data
That location has these operators that come from the same table.
The expected results is this
Any ideas?

You seem to want a join:
select
o.location,
o.aptdate,
case when o.aptstatus = 3 then '08:00:00' else o.apttime end as apttime,
case when o.aptstatus = 3 then 540 else o.aptlength end as aptlength,
o.aptstatus,
o.operatory,
a.operatory as operatorynum
from rpt_officeschedules o
inner join apptwidget_operatorie a on a.location = o.location
where
o.apttime between '07:59:00' and '17:30:00'
and o.aptdate between '2020-11-07' and '2020-11-08'
and (o.operatory like '%np%' or o.operatory like '%open%' or o.operatory is null)
order by o.location, o.aptdate, o.operatorynum, o.apttime, o.operatory;

I think you might want to cycle through the second table assigning the values in a round-robin fashion. If so, you can use window functions:
select os.Location, os.AptDate,
(case when os.AptStatus = 3 then '08:00:00' else os.AptTime end) as AptTime,
(case when os.AptStatus = 3 then 540 else os.AptLength end) as AptLength,
os.AptStatus, os.OperatoryNum, os.Operatory
from (select os.*,
row_number() over (partition by location order by atpdate) as seqnum
from rpt_officeschedules os
where os.AptTime between '07:59:00' and '17:30:00' and
os.AptDate between '2020-11-07' and '2020-11-08' and
(Operatory LIKE '%NP%' or Operatory LIKE '%OPEN%'or Operatory is null )
) os join
(select aw.*, count(*) over (partition by location) as cnt,
row_number() over (partition by location order by location) as seqnum
from appwidget ap
) ap
on os.location = ap.location and
(os.seqnum - 1) % sp.cnt = ap.seqnum - 1
order by os.Location, os.AptDate, os.OperatoryNum, os.AptTime, os.Operatory;

Related

Join not summing CASE WHEN

What I'm looking to do is show my current performance for this month, compared with expected scheduled wins to come in and then display the total expected amount, by product type.
For clarity, I have two sub-products that I'm grouping under the same name.
My issue is that for my 'Charged' amount, it's keeping the two sub-products separate, where as the 'Scheduled' amount is working fine.
The table should look like:
Type | Charged | Scheduled | Expected
A 3 2 5
B 1 1 2
What's actually showing is:
Type | Charged | Scheduled | Expected
A 2 1 3
A 1 1 2
B 1 1 2
The code is as follows:
select
t2.product,
t1.Charged,
t2.Scheduled,
t1.charged + t2.scheduled as 'expected'
from(
select
case
when user_type = 'a1' then 'a'
when user_type = 'a2' then 'a'
else 'b'
end as 'Type',
SUM(charged) as 'Scheduled'
from
table
where
month(date) = month(now())
and
year(date) = year(now())
and status like 'scheduled'
group by 1
order by 2 desc) t2 join
(select
case
when user_type = 'a1' then 'a'
when user_type = 'a2' then 'a'
else 'b'
end as 'Type',
sum(charged) as 'Charged'
FROM table
WHERE (status = 'Complete'
AND str_to_date(concat(date_format(date, '%Y-%m'), '-01'), '%Y-%m-%d') = str_to_date(concat(date_format(now(), '%Y-%m'), '-01'), '%Y-%m-%d'))
GROUP BY user_type
ORDER BY user_type ASC) as t1 on t1.type = t2.type
I appreciate I might not be explaining this incredibly well (and that my code is probably quite clunky - I'm still fairly new!) so any help/direction would be appreciated.
Thanks!
Just some suggestion
you have a column product in main select but you have type in subquery and not product
you should not use sigle quote around column name
ad you have group by user_type but you need group by type for charged
select
t2.type,
t1.Charged,
t2.Scheduled,
t1.charged + t2.scheduled as 'expected'
from(
select
case
when user_type = 'a1' then 'a'
when user_type = 'a2' then 'a'
else 'b'
end as Type,
SUM(charged) as Scheduled
from
table
where
month(date) = month(now())
and
year(date) = year(now())
and status like 'scheduled'
group by 1
order by 2 desc) t2 join
(select
case
when user_type = 'a1' then 'a'
when user_type = 'a2' then 'a'
else 'b'
end as Type,
sum(charged) as Charged
FROM table
WHERE (status = 'Complete'
AND str_to_date(concat(date_format(date, '%Y-%m'), '-01'), '%Y-%m-%d') = str_to_date(concat(date_format(now(), '%Y-%m'), '-01'), '%Y-%m-%d'))
GROUP BY Type
ORDER BY Type ASC) as t1 on t1.type = t2.type

MySQL Group By Query X or not X

This should be easy but I have not found the answer. I have a query that is running a count and I want to group this within the query into those where State = 1 and State <>1 and have the sum for each group.
SELECT count(`id_job`) as count, `state`
FROM job_table
GROUP BY `state`;
You can query a boolean expression and group by it too:
SELECT state = 1, COUNT(*)
FROM job_table
GROUP BY state = 1
SELECT count(id_job) as count,state FROM job_table
GROUP BY case when state = 1 then 1 else 0
In MySQL, you can easily put these into columns:
select sum( state = 1 ) as state_1,
sum( state <> 1 ) as state_2
from job_table;
If state can be NULL, you want to be a bit careful. The second condition is safer as:
select sum( state = 1 ) as state_1,
sum( not state <=> 1 ) as state_2
from job_table;
Try this
SELECT SUM(CASE WHEN State=1 THEN 1 END) as count1,
SUM(CASE WHEN State<>1 THEN 1 END) as count2,
State
FROM job_table
GROUP BY state

COUNT multiple types of same column

In my current query:
SELECT COUNT(WC.ID) AS "Regions"
FROM WHOLE_FEATURES_PDB_CHAINS AS WC
;
I COUNT(WC.ID) AS "Regions" .
However, we have multiple regions with WC.Type can be 1,2,3,4. I need to count each type occurrence into COUNT(WC.ID) AS "Region_1", COUNT(WC.ID) AS "Region_2" ... depending on WC.Type.
Is there any way to solve this in one query? I am looking at MySQL IF, yet do not know how to integrate it into the count function.
I need it to be in one row (the shown query here is reduced, it's a larger query)
SELECT COUNT(WC.ID) AS "Region_1" , COUNT(WC.ID) AS "Region_2" ...
Here is the complete query if anyone is interested:
SELECT PCS.PDB_id, PCS.Chain, PPA.ENSEMBL_start, PPA.ENSEMBL_end, PPA.eValue, PIN.TITLE AS "pdbTitle", COUNT(WC.ID) AS "Regions"
FROM PDB_Chains AS PCS
LEFT JOIN WHOLE_FEATURES_PDB_CHAINS AS WC ON WC.PDB_CHAIN_ID = PCS.idPDB_chains, PDB_protein_alignment PPA, PDB_INFOS PIN
WHERE PCS.idPDB_chains = PPA.idPDB_Chains
AND PCS.PDB_id = PIN.PDB_ID
AND PPA.idProteins = (SELECT idProteins from Proteins WHERE ENSEMBL_protein_id = "'+submittedID+'")
GROUP BY PCS.PDB_id, PCS.Chain ORDER BY PCS.PDB_id;
Here's the working solutin based on your kind answers
SELECT PIN.TITLE AS "pdbTitle", COUNT(CASE WHEN WC.STRUCTURAL_FEATURES_ID = 1 then 1 end) AS "PPInterface" , COUNT(CASE WHEN WC.STRUCTURAL_FEATURES_ID = 4 then 1 end) AS "flexibleRegions"
FROM PDB_Chains AS PCS LEFT JOIN WHOLE_FEATURES_PDB_CHAINS AS WC ON WC.PDB_CHAIN_ID = PCS.idPDB_chains, PDB_protein_alignment PPA, PDB_INFOS PIN
WHERE PCS.idPDB_chains = PPA.idPDB_Chains
AND PCS.PDB_id = PIN.PDB_ID
AND PPA.idProteins = (SELECT idProteins from Proteins WHERE ENSEMBL_protein_id = "ENSP00000256078.4")
GROUP BY PCS.PDB_id, PCS.Chain ORDER BY PCS.PDB_id;
You can use case when statement inside your aggregate function.
Try this .
count(case when WC.type = 1 then 1 end) as region_1, similarly repeat for another column.
Select
...
...
sum(if WC.ID = 1 then 1 else 0) as Region1,
sum(if WC.ID = 2 then 1 else 0) as Region2,
sum(if WC.ID = 3 then 1 else 0) as Region3,
sum(if WC.ID = 4 then 1 else 0) as Region4
Might do what you want.
You can use GROUP BY with COUNT to get the required result, e.g.:
SELECT WC.Type, COUNT(WC.ID) AS "Regions"
FROM WHOLE_FEATURES_PDB_CHAINS AS WC
GROUP BY WC.Type;
Update
If you want the counts as pivoted column for each region then you can write inner SELECT queries, e.g.:
SELECT
(SELECT COUNT(ID) FROM WHOLE_FEATURES_PDB_CHAINS WHERE type = 1) AS "Region_1",
(SELECT COUNT(ID) FROM WHOLE_FEATURES_PDB_CHAINS WHERE type = 2) AS "Region_2",
other_column
FROM WHOLE_FEATURES_PDB_CHAINS AS WC
WHERE <some condition>;

SQL query: Separate column where IDs are the same but type is different

Basically I get this from a simple select query:
SELECT Site.RefID, SiteName, Dates.Date, Dates.Type
FROM Site, Dates
WHERE Site.RefID = Dates.RefID;
RefID | SiteName | Date | Type
1 Sydney 06-12-15 OPENED
1 Sydney 08-12-15 CLOSED
2 Mel 17-12-15 OPENED
2 Mel 19-12-15 CLOSED
But I want to seperate it so tge result is similar to this:
RefID | SiteName | DateOPENED | DateCLOSED
1 Sydney 06-12-15 | 08-12-15
Basically I want to compare the data tracking details
Apologies in advance if this question isn't structured very well :/ I'm a complete beginner
I was thinking maybe a select within a select or possible case when's, but I can't seem to get either working
Try the following approach, using case expression:
select s.RefID
, s.Name
, min(case when d.Type == 'OPENED' then d.Date end) as DateOPENED
, min(case when d.Type == 'CLOSED' then d.Date end) as DateCLOSED
from Site s
join Dates d on s.RefID = d.RefID
group by s.RefID, s.Name
You can use conditional aggregation to get expected result:
SELECT Site.RefID, SiteName,
MIN(CASE WHEN Dates.Type = 'OPENED' THEN Dates.Date END) DateOPENED,
MAX(CASE WHEN Dates.Type = 'CLOSED' THEN Dates.Date END) DateCLOSED
FROM Site
INNER JOIN Dates ON Site.RefID = Dates.RefID
GROUP BY Site.RefID, SiteName
Also, it is always preferable to use explicit instead of implicit join syntax.
Add a GROUP BY to your current query, use MIN for opening date, and MAX for en closing date.
SELECT Site.RefID, SiteName, MIN(Dates.Date) as DateOPENED, MIN(Dates.Date) as DateCLOSED
FROM Site
JOIN Dates ON Site.RefID = Dates.RefID
group by fID, SiteName
Alternatively, JOIN once for opening and once for closing:
SELECT Site.RefID, SiteName, do.Date as DateOPENED, dc.Date as DateCLOSED
FROM Site
LEFT JOIN (select Refid, Date from Dates where Type = 'OPENED') do ON Site.RefID = do.RefID
LEFT JOIN (select Refid, Date from Dates where Type = 'CLOSED') dc ON Site.RefID = dc.RefID
SELECT A.RefId, A.SiteName, A.Date DateOpened, B.Date DateClosed
FROM #tbl A JOIN #tbl B
ON A.RefId = B.RefID
AND A.Type = 'OPENED'
AND B.Type = 'CLOSED'
For the sake of simplicity, have replaced the query with #tbl(you can deal with it howsoever you'd like to).

how to join after left join complex mysql queries

I have this query
SELECT
currency_code,
SUM(CASE WHEN TYPE = 'buy'THEN to_amount END ) AS BUY,
SUM(CASE WHEN TYPE = 'sell' THEN to_amount END ) AS SELL,
SUM(CASE WHEN TYPE = 'sell' THEN rate END ) AS SELL_RATE,
SUM(CASE WHEN TYPE = 'buy' THEN rate END ) AS BUY_RATE,
AVG(CASE WHEN TYPE = 'buy' THEN rate END ) AS AVG_BUY_RATE,
AVG(CASE WHEN TYPE = 'sell' THEN rate END ) AS AVG_SELL_RATE
FROM tb_currency
LEFT JOIN tb_bill
ON tb_currency.CURRENCY_ID = tb_bill.CURRENCY_ID
AND tb_bill.TYPE IN ('buy', 'sell')
AND date( DATE_TIME ) >= '2011-01-01'
AND date( DATE_TIME ) <= '2011-01-11'
GROUP BY currency_code
that will output this:
Right now i want to join this query with another table called tb_user
the tb_user have PK called user_id and the tb_bill that is use in the query above also have foreign key called user_id
tb_user
user_id (pk)| user_name | branch_id
tb_bill
bill_id (pk) | user_id (fk)|
Desired result should be the above picture plus one column branch_id.
If it doesnt have branch_id, return null.
I tried several times but still cant join it correctly. Hope you guys can help.
Thanks.
The three conditions in the join (the AND clauses) might be giving you trouble. Those three conditions are selection criteria, not join criteria.
Also, your use of CASE looks odd to me. I'm sure it works, but IF might be better suited for a one-condition function. In the below, if the fields are floating point rather than integer then replace the 0 with 0.0.
SELECT currency_code,
SUM(IF(TYPE = 'buy', to_amount, 0)) AS BUY,
SUM(IF(TYPE = 'sell', to_amount, 0)) AS SELL,
SUM(IF(TYPE = 'sell', rate, 0)) AS SELL_RATE,
SUM(IF(TYPE = 'buy', rate, 0)) AS BUY_RATE,
AVG(IF(TYPE = 'buy', rate, 0)) AS AVG_BUY_RATE,
AVG(IF(TYPE = 'sell', rate, 0)) AS AVG_SELL_RATE,
tb_user.whatever_field,
tb_user.whatever_other_field
FROM tb_currency
LEFT JOIN tb_bill ON tb_currency.CURRENCY_ID = tb_bill.CURRENCY_ID
LEFT JOIN tb_user ON tb_bill.user_id = tb_user.user_id
WHERE tb_bill.TYPE IN ('buy', 'sell')
AND date( DATE_TIME ) >= '2011-01-01'
AND date( DATE_TIME ) <= '2011-01-11'
GROUP BY currency_code, tb_user.user_id
Finally, all-cap field names look odd to my eye as well. Whatever works for you though.
add user_id to SELECT part
after
LEFT JOIN tb_bill ON tb_currency.CURRENCY_ID = tb_bill.CURRENCY_ID
place
LEFT JOIN tb_user ON tb_user.id = tb_bill.user_id
also you missing WHERE ( put instead first AND )
and
GROUP BY currency_code, user_id