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