SQL: Add multiple where clauses in a single query - mysql

I want to run an SQL query from Node.js. I am currently showing the total number of projects that have a specific status in each of the 4 quarters. What I want to do now is show the same result but while adding one more condition i.e, Fiscal Year.
Here's my code for the 4 quarters:
SELECT
SUM(CurrentStatus = 'On Hold') onHold_Q,
SUM(CurrentStatus = 'In Progress') inProgress_Q,
SUM(CurrentStatus = 'Not Started') notStarted_Q,
SUM(CurrentStatus = 'Completed') completed_Q,
SUM(CurrentStatus = 'Routine Activity') routineActivity_Q,
SUM(CurrentStatus = 'Done But Not Published') doneButNotPublished_Q
FROM office.officedata
WHERE Quarter in ('Q1', 'Q2', 'Q3', 'Q4') //here I want to add one more condition FiscalYear in ('2016-17')
GROUP BY Quarter
ORDER BY Quarter;
This prints output in 4 different rows which is exactly what I want. But, when I add one more condition then it does execute but only one row gets printed since the projects in the rest of the 3 quarters for that year do not exist. I want to somehow print 4 different rows for quarters for every Fiscal Year. The query should print 0 in the row if no projects exist for that quarter in a year. How can I do that? My SQL is not that strong, it would be great if someone can help me out.

Can you tell me programmatically how to do that?
SELECT years.FiscalYear, quarters.Quarter,
SUM(CurrentStatus = 'On Hold') onHold_Q,
SUM(CurrentStatus = 'In Progress') inProgress_Q,
SUM(CurrentStatus = 'Not Started') notStarted_Q,
SUM(CurrentStatus = 'Completed') completed_Q,
SUM(CurrentStatus = 'Routine Activity') routineActivity_Q,
SUM(CurrentStatus = 'Done But Not Published') doneButNotPublished_Q
FROM (SELECT 2016 FiscalYear UNION SELECT 2017) years
CROSS JOIN (SELECT 'Q1' Quarter UNION SELECT 'Q2' UNION SELECT 'Q3' UNION SELECT 'Q4') quarters
LEFT JOIN office.officedata ON office.FiscalYear = years.FiscalYear AND office.Quarter = quarters.Quarter
GROUP BY years.FiscalYear, quarters.Quarter
ORDER BY years.FiscalYear, quarters.Quarter;
Maybe SUMs must be wrapped with COALESCE() for to replace NULLs to zeros.

Related

How do i add SUM with multiple WHERE condition?

I am currently facing an issue with multiple WHERE condition
I am trying to do this condition.
SUM(IF SUM(B.answer) is lesser than 5),
SUM(IF SUM(B.answer) is between 6-10),
SUM(IF SUM(B.answer) is between 11-15),
SUM(IF SUM(B.answer) is greater than 16)
GROUP BY A.schlvl
Expecting a result like this:
Hope someone get the idea of my explanation
SELECT DISTINCT A.schlvl AS school, COUNT(DISTINCT(A.name)) AS NoOfChild, CASE WHEN SUM(B.answer)<5 THEN SUM(B.answer) END FROM child AS A INNER JOIN question AS B ON A.child_id = B.child_id
WHERE SUM(B.answer) IN (<5,>=6 && <=10,>=11 && <=15,>=16)
GROUP BY A.schlvl
Instead of using SUM inside a CASE WHEN, what you can do is SUM the CASE WHEN.
In this case however, when looking at the screenshot of the expected result?
I think it needs COUNT's instead.
Example:
SELECT
A.schlvl AS [School Level],
COUNT(DISTINCT(A.name)) AS [Total No. Of Children],
COUNT(DISTINCT CASE WHEN B.sum_answer <= 5 THEN A.name END) AS [le 5],
COUNT(DISTINCT CASE WHEN B.sum_answer between 6 and 10 THEN A.name END) AS [6 to 10],
COUNT(DISTINCT CASE WHEN B.sum_answer between 11 and 15 THEN A.name END) AS [11 to 15],
COUNT(DISTINCT CASE WHEN B.sum_answer >= 16 THEN A.name END) AS [ge 16]
FROM child AS A
LEFT JOIN
(
SELECT child_id, SUM(answer) AS sum_answer
FROM question
GROUP BY child_id
) AS B ON A.child_id = B.child_id
GROUP BY A.schlvl
Also, if a limit on an aggregation (f.e. SUM, COUNT) is needed, then one would do that in the HAVING clause.
I currently think it's not needed here.
Since getting the ranges of <=5 or between 6 and 15 or >=16 is hardly a limit. You need them all?
if you need a filter on aggregated result you must use having and not where
and you should use a valid or clause not a in clause with range
SELECT DISTINCT A.schlvl AS school
, COUNT(DISTINCT(A.name)) AS NoOfChild
, CASE WHEN SUM(B.answer)<5 THEN SUM(B.answer) END
FROM child AS A
INNER JOIN question AS B ON A.child_id = B.child_id
HAVING SUM(B.answer) IN (<5,>=6 && <=10,>=11 && <=15,>=16)
HAVING SUM(B.answer) < 5
OR (SUM(B.answer) >=6 AND SUM(B.answer) <=10)
OR (SUM(B.answer) >=11 AND SUM(B.answer) <=15)
OR (SUM(B.answer) >=16)
GROUP BY A.schlvl

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>;

How to query last 2 business days only

I'm running a query that pulls the correct information I'm looking for, but I need it to pull the last 2 business days rather than the last 2 days. This comes into play when it's Monday and my results show information for Monday and Sunday rather than Monday and Friday. How can I change my query to pull in business days only?
USE [LetterGeneration]
SELECT g.LetterGenerationPrintJobId
,CAST(t.[TemplateKey] AS VarChar) AS LetterCode
,convert(char(12),r.CreatedDate,101) AS CreatedDate
,s.LetterGenerationStatusId AS Status
,s.StatusKey AS StatusDesc
,count(g.LetterGenerationId) as LetterCount
,c.BankingDateYorN
FROM [LetterGenerationTemplateRequest] AS r
INNER JOIN [LetterGenerationTemplate] AS t
ON t.[LetterGenerationTemplateId] = r.LetterGenerationTemplateId
INNER JOIN LetterGeneration g
ON g.LetterGenerationTemplateRequestId = r.LetterGenerationTemplateRequestId
INNER JOIN LetterGenerationStatus s
ON g.LetterGenerationStatusId = s.LetterGenerationStatusId
INNER JOIN Enterprise..Calendar C
ON c.BeginDate = g.LetterDate
WHERE ((DATEDIFF(d, r.CreatedDate, GETDATE()) = 0) OR (DATEDIFF(d, r.CreatedDate, GETDATE()) = 1))
--BankingDateYorN = 1
--AND RelativeTimeValue_BusinessDates =-1
AND t.[TemplateKey] NOT LIKE '%PLTV1%'
AND s.LetterGenerationStatusId NOT LIKE '4'
AND s.LetterGenerationStatusId NOT LIKE '16'
AND s.LetterGenerationStatusId NOT LIKE '19'
AND s.LetterGenerationStatusId NOT LIKE '20'
AND s.LetterGenerationStatusId NOT LIKE '38'
GROUP BY r.[LetterGenerationTemplateRequestId]
,r.LetterGenerationTemplateId
,g.Lettergenerationprintjobid
,t.[TemplateKey]
,r.[Loan_no]
,r.CreatedDate
,r.[CreatedBy]
,s.LetterGenerationStatusId
,s.StatusKey
,c.BankingDateYorN
ORDER BY r.CreatedDate DESC
UPDATE: I've recently discovered how to join a calendar table to my current query. The calendar query has a column called BusinessDayYorN with 1's for a business day and 0's for weekends and holidays. I've also updated the old query to now include the join.
select *
from LetterGenerationTemplateRequest
where createddate >= (
getdate() -
case datename(dw,getdate())
when 'Tuesday' then 5
when 'Monday' then 4
else 3
end
)
--and datename(dw,createdDate) not in ('Saturday','Sunday',datename(dw,getdate()))
and datename(dw,createdDate) not in ('Saturday','Sunday')
;
Assuming that you always want to include the last two non-weekend days you can try this:
; with aux as (
select diff = case
when datename(weekday, getdate()) in ('Tuesday', 'Wednesday ', 'Thursday', 'Friday') then 1
else
case datename(weekday, getdate())
when 'Saturday' then 2
when 'Sunday' then 3
when 'Monday' then 4
end
end
)
SELECT --r.[LetterGenerationTemplateRequestId]
--,r.LetterGenerationTemplateId
g.LetterGenerationPrintJobId
,CAST(t.[TemplateKey] AS VarChar) AS LetterCode
,r.[Loan_no]
,convert(char(12),r.CreatedDate,101) AS CreatedDate
-- ,g.ModifiedDate
-- ,convert(varchar(18), g.ModifiedDate - r.CreatedDate, 108) AS TimeSpan
,s.LetterGenerationStatusId AS Status
,s.StatusKey AS StatusDesc
,count(g.LetterGenerationId) as LetterCount
FROM [LetterGenerationTemplateRequest] AS r
INNER JOIN [LetterGenerationTemplate] AS t
ON t.[LetterGenerationTemplateId] = r.LetterGenerationTemplateId
INNER JOIN LetterGeneration g
ON g.LetterGenerationTemplateRequestId = r.LetterGenerationTemplateRequestId
INNER JOIN LetterGenerationStatus s
ON g.LetterGenerationStatusId = s.LetterGenerationStatusId
WHERE
DATEDIFF(day, r.CreatedDate, GETDATE()) <= (select diff from aux)
AND t.[TemplateKey] NOT LIKE '%PLTV1%'
AND s.LetterGenerationStatusId NOT LIKE '4'
AND s.LetterGenerationStatusId NOT LIKE '16'
AND s.LetterGenerationStatusId NOT LIKE '19'
AND s.LetterGenerationStatusId NOT LIKE '20'
AND s.LetterGenerationStatusId NOT LIKE '38'
GROUP BY r.[LetterGenerationTemplateRequestId]
,r.LetterGenerationTemplateId
,g.Lettergenerationprintjobid
,t.[TemplateKey]
,r.[Loan_no]
,r.CreatedDate
-- ,g.ModifiedDate
,r.[CreatedBy]
,s.LetterGenerationStatusId
,s.StatusKey
ORDER BY r.CreatedDate DESC
The CTE aux returns a dataset with only one record and only one field, the value of which is the number of days you need to go back in your WHERE statement.

Mysql exclude saturday where another value is equal to

Im having a little trouble coming to the correct syntax for my code to do what I want. Im summing up numbers of hour worked in a specific week by an employee, but I want to exclude certain days from my query. The employee can have worked for different clients, which are defined by a client_id.
Now the code below have a syntax error in the WHERE-statements inside the parantheses, and I know why, Ill just let them be there for the sake of explaining what I want to achieve in this case.
I appreciate any help you can give me.
Btw: I also tried AND( DAYOFWEEK(date) <> 7 AND client_id = '1' ) but this doesnt work either. I recon this is because my query return just a single line of result, and thus my above code doesnt do anything while the query is loop
SELECT
date,
client_id,
SUM(hours)-37.5 AS total,
SUM(lunch = 'yes') AS lunch
FROM
hours
WHERE
CONCAT(WEEK(date, 1)) = '25'
AND
CONCAT(YEAR(date)) = '2015'
AND
employee_id = '14'
AND ( DAYOFWEEK(date) <> 7 WHERE client_id = '1' )
AND ( DAYOFWEEK(date) <> 1 WHERE client_id = '2' )
AND
status = 'billed'
HAVING SUM(hours) > 37.5
You can use a bunch of logical not and and logical operators to build this logic:
SELECT
date,
client_id,
SUM(hours)-37.5 AS total,
SUM(lunch = 'yes') AS lunch
FROM
hours
WHERE
CONCAT(WEEK(date, 1)) = '25'
AND
CONCAT(YEAR(date)) = '2015'
AND
employee_id = '14'
AND NOT ( DAYOFWEEK(date) = 7 AND client_id = '1' )
AND NOT ( DAYOFWEEK(date) = 1 AND client_id = '2' )
AND
status = 'billed'
HAVING SUM(hours) > 37.5

How to create query that groups Years

I have this query:
SELECT Count(tblpeople.PersonID) AS Total,
YEAR(CURRENT_TIMESTAMP) - YEAR(tblmembership.MemberSince) AS YearsMember
FROM tblmembership
INNER JOIN tblpeople ON tblmembership.PersonID = tblpeople.PersonID
WHERE
tblpeople.MemSTATUS = 'Back from VPM'
OR tblpeople.MemSTATUS = 'Sent To VPM 2'
OR tblpeople.MemSTATUS = 'Sent To VPM 1'
OR tblpeople.MemSTATUS = 'Back from VPM'
OR tblpeople.MemSTATUS = 'Renewal'
OR tblpeople.MemSTATUS = 'Active Member'
GROUP BY YearsMember
HAVING YearsMember
ORDER BY YearsMember
It gets counts on years people have been a member.
What has me stumped is the boss wants it broken down by:
0 - 2,
3 -5,
6 - 10,
11 - 20,
>20
So counts for each. I can not figure out how to do this, though I'm sure there's a way!
I can just bring this into excel and manually group the counts, but wanted it done all in one shot.
Thanks
Use a case when expression: http://docs.oracle.com/cd/B19306_01/server.102/b14200/expressions004.htm
case when years between 0 and 2 then ... else ...
Use IN instead of all those OR statements. Then use a CASE statement to group your results:
SELECT Count(tblpeople.PersonID) AS Total,
CASE
WHEN YEAR(CURRENT_TIMESTAMP) - YEAR(tblmembership.MemberSince) BETWEEN 0 AND 2
THEN '0-2'
WHEN YEAR(CURRENT_TIMESTAMP) - YEAR(tblmembership.MemberSince) BETWEEN 3 AND 5
THEN '3-5',
...
WHEN YEAR(CURRENT_TIMESTAMP) - YEAR(tblmembership.MemberSince) > 20
THEN '>20'
END AS YearsMember
FROM tblmembership
INNER JOIN tblpeople ON tblmembership.PersonID = tblpeople.PersonID
WHERE tblpeople.MemSTATUS IN ( 'Back from VPM', 'Sent To VPM 2', 'Sent To VPM 1',
'Back from VPM', 'Renewal', 'Active Member')
GROUP BY YearsMember
ORDER BY YearsMember
SQL Fiddle Demo