Multiple SQL Counts in with multiple criteria - sql

I've been trying to optimise the way I retrieve data from my database for display on a "dashboard" type of page for software development
My database structure is as follows:
Requirements Table that contains all the various requirements with various fields, but importantly a REQ_ID as key.
Tasks Table that contains can contain multiple tasks with a TASK_ID, TASK_NAME (DEV, TEST OR RELEASE), TASK_STATUS (Not Started, Complete, Blocked), TASK_WINDOW (Week1, Week2, .. etc. when task was completed) and a link back to a requirement with REQ_I. For example, a requirement may have multiple dev tasks, test tasks and release tasks but for can only be dev complete if all the dev tasks related to a requirement is complete, otherwise it is incomplete
I would like to query these two tables to provide me a results set that contains individually the number DEV Complete, Test Complete and Release Complete requirements per DEV task window in a single query. I'm currently performing multiple query each containing subqueries and then aggregating the results with PHP, however this in total takes 15 sec to exec, Can anybody please help me in consolidating this into a single query>

SELECT r.REQ_ID,
SUM(CASE WHEN t.TASK_NAME = 'DEV' THEN 1 ELSE 0 END) AS DevComplete,
SUM(CASE WHEN t.TASK_NAME = 'TEST' THEN 1 ELSE 0 END) AS TestComplete,
SUM(CASE WHEN t.TASK_NAME = 'RELEASE' THEN 1 ELSE 0 END) AS ReleaseComplete
FROM Requirements r
INNER JOIN Tasks t
ON r.REQ_ID = t.REQ_ID
WHERE t.TASK_STATUS = 'Complete'
GROUP BY r.REQ_ID

I realize this is an old question but I ran a test with the following patterns:
Pattern 1:
SELECT
[Count1] = SUM(CASE WHEN ... THEN 1 ELSE 0 END),
[Count2] = SUM(CASE WHEN ... THEN 1 ELSE 0 END)
FROM
[Table]
GROUP BY
[Field]
Pattern 2:
SELECT
[COUNT1] = (SELECT COUNT(*) FROM [Table] WHERE ...),
[Count2] = (SELECT COUNT(*) FROM [Table] WHERE ...)
In my case, when running both queries, pattern 2 took 36 % of the time and pattern 1 took 64%. To me, pattern 1 looks more elegant, but it didn't perform nearly as well in my scenario.

Related

Trying to grab data from two columns and format them properly

So I have a database here with a table that lists off whether or not certain processes have failed. There are two columns, IsProcessed, and IsFailed. A failed process can still be considered processed if the error was handled, but I still need to recognize that it failed. They're both bit values, and so I have to try and grab and separate them despite that they may depend on one another. After they've been separated out, I need to count the relative successes and relative failures.
I utilize an AND statement in my WHERE clause to try and separate out the successes from the failures. I honestly have no idea where to go from here.
SELECT CAST(PQ.ProcessedDate AS Date) AS Date, COUNT(PQ.IsProcessed) AS Successes
FROM PQueue PQ
WHERE PQ.ProcessDate BETWEEN '2019-10-1' AND '2019-10-31' AND PQ.IsFailed = 0 AND PQ.IsProcessed = 1
GROUP BY CAST(PQ.ProcessDate AS Date)
ORDER BY CAST(PQ.ProcessDate AS Date) ASC
Because a failed process can still be processed in the system, we have to do a check first to try and grab the data that was processed but didn't flag a failure. Now I need to try and find a way to not exclude the failures, but include them and place them in a group. I can do the group part, but I'm relatively new to SQL so I don't know whether or not I can place something in an IF statement somewhere or try to use variables to get this done. Thank you in advance.
You seem to want conditional aggregation:
SELECT CAST(PQ.ProcessedDate AS Date) AS Date,
SUM(CASE WHEN PQ.IsFailed = 0 AND PQ.IsProcessed = 1 THEN 1 ELSE 0 END) as Successes,
SUM(CASE WHEN PQ.IsFailed = 1 AND PQ.IsProcessed = 1 THEN 1 ELSE 0 END) as Fails
FROM PQueue PQ
WHERE PQ.ProcessDate BETWEEN '2019-10-1' AND '2019-10-31'
GROUP BY CAST(PQ.ProcessDate AS Date)
ORDER BY CAST(PQ.ProcessDate AS Date) ASC
If SQL Server then maybe a CASE statement would help you out.
eg
SELECT ...........
CASE
WHEN IsFailed = 1 AND IsProcessed = 1 THEN "Processed But Failed"
WHEN IsFailed = 0 AND IsProcessed = 0 THEN "Not Processed"
WHEN IsFailed = 0 AND IsProcessed = 1 THEN "Processed Succesfully"
WHEN IsFailed = 1 AND IsProcessed = 0 THEN "Failed"
END as REsult

SQL query question. Extracting data met for one of two conditions but not both

I'm extracting student data who have completed a list of courses for degree requirements. One of the courses on the list is equivalent to another course, so if a student completes both equivalent courses, it can only be counted once towards a degree. I need to extract data on students who completed the list of courses, while filtering for just one of the equivalent courses.
Where am I going wrong?
I've tried different OR and AND NOT clauses but I can't seem to get the result that I need
use coll18_live
select ENR_STUDENT_ID, ENR_TERM, CRS_NAME, ENR_GRADE
from dbo.CA320_ENROLLMENT_VIEW_N03
WHERE ENR_CENSUS_REG_FLAG = 'Y'
and ENR_TERM in ('14/FA', '15/SP')
and not (CRS_NAME = 'BUSI-105' and CRS_NAME = 'ENGL-120')
and CRS_NAME in ('ACCT-120', 'ACCT-125', 'BUSI-100', 'BUSI-103', 'BUSI-105', 'ENGL-120')
I expect the output to show students who completed ACCT-120, ACCT-12, BUSI-100, BUSI-103, and BUSI-105 or ENGL-120 (but not both BUSI-105 or ENGL-120)
I think you want aggregating with a having clause. You cannot do this with a WHERE, because the information you want is (apparently) in different rows:
select ENR_STUDENT_ID
from dbo.CA320_ENROLLMENT_VIEW_N03
where ENR_CENSUS_REG_FLAG = 'Y' AND
ENR_TERM in ('14/FA', '15/SP')
group by ENR_STUDENT_ID
having sum(case when CRS_NAME in ('ACCT-120', 'ACCT-125', 'BUSI-100', 'BUSI-103') then 1 else 0 end) = 4 and
sum(case when CRS_NAME in ('BUSI-105', 'ENGL-120') then 1 else 0 end) > 0;

Query data not represented by model classes using Entity Framework Core

I stumbled accross this site about limitations of the Entity Framework (Core) stating one can only select data represented by model classes.
Now I want to implement something basic like statistics holding sums and counts of mulitple fields in multiple tables. Currently I am using multiple selects (one for each field in each table) which is not optimal because the statistics are placed on each page and therefor queried often.
I created the following SQL statement which queries the result with one call but I can't figure out to execute it from the MVC controller and return it.
SELECT
SUM(CASE WHEN temp.val1 = 1 THEN 1 ELSE 0 END) res1,
SUM(CASE WHEN temp.val1 = 2 THEN 1 ELSE 0 END) res2,
SUM(temp.valT) res3
FROM (
SELECT x.Field1 AS val1, x.Field2 AS valT FROM Table1 x
UNION ALL
SELECT 0 AS val1, y.Field2 AS valT FROM Table2 y
UNION ALL
SELECT 0 AS val1, z.Field2 AS valT FROM Table3 z
) temp
Use may use libraries like Dapper to execute this query and map it to non-model class:

SQL Server Update via Select Statement

I have the following sql statement and I want to update a field on the rows returned from the select statement. Is this possible with my select? The things I have tried are not giving me the desired results:
SELECT
Flows_Flows.FlowID,
Flows_Flows.Active,
Flows_Flows.BeatID,
Flows_Flows.FlowTitle,
Flows_Flows.FlowFileName,
Flows_Flows.FlowFilePath,
Flows_Users.UserName,
Flows_Users.DisplayName,
Flows_Users.ImageName,
Flows_Flows.Created,
SUM(CASE WHEN [Like] = 1 THEN 1 ELSE 0 END) AS Likes,
SUM(CASE WHEN [Dislike] = 1 THEN 1 ELSE 0 END) AS Dislikes
FROM Flows_Flows
INNER JOIN Flows_Users ON Flows_Users.UserID = Flows_Flows.UserID
LEFT JOIN Flows_Flows_Likes_Dislikes ON
Flows_Flows.FlowID=Flows_Flows_Likes_Dislikes.FlowID
WHERE Flows_Flows.Active = '1' AND Flows_Flows.Created < DATEADD(day, -60, GETDATE())
Group By Flows_Flows.FlowID, Flows_Flows.Active, Flows_Flows.BeatID,
Flows_Flows.FlowTitle, Flows_Flows.FlowFileName, Flows_Flows.FlowFilePath,
Flows_Users.UserName, Flows_Users.DisplayName, Flows_Users.ImageName,
Flows_Flows.Created
Having SUM(CASE WHEN [Like] = 1 THEN 1 ELSE 0 END) = '0' AND SUM(CASE WHEN [Dislike] = 1
THEN 1 ELSE 0 END) >= '0'
This select statement returns exactly what I need but I want to change the Active field from 1 to 0.
yes - the general structure might be like this: (note you don't declare your primary key)
UPDATE mytable
set myCol = 1
where myPrimaryKey in (
select myPrimaryKey from mytable where interesting bits happen here )
Because you haven't made your question more clear in what result you want to achieve, I'll provide an answer with my own assumptions.
Assumption
You have a select statement that gives you stuffs, and it works as desired. What you want it to do is to make it return results and update those selected rows on the fly - basically like saying "find X, tell me about X and make it Y".
Anwser
If my assumption is correct, unfortunately I don't think there is any way you can do that. A select does not alter the table, it can only fetch information. Similarly, an update does not provide more detail than the number of rows updated.
But don't give up yet, depending on the result you want to achieve, you have alternatives.
Alternatives
If you just want to update the rows that you have selected, you can
simply write an UPDATE statement to do that, and #Randy has provided
a good example of how it will be written.
If you want to reduce calls to server, meaning you want to make just
one call to the server and get result, as well as to update the
rows, you can write store procedures to do that.
Store procedures are like functions you wrote in programming languages. It essentially defines a set of sql operations and gives them a name. Each time you call that store procedure, the set of operations gets executed with supplied inputs, if any.
So if you want to learn more about store procedures you can take a look at:
http://www.mysqltutorial.org/introduction-to-sql-stored-procedures.aspx
If I understand correctly you are looking for a syntax to be able to select the value of Active to be 0 if it is 1. The syntax for something like that is
SELECT
Active= CASE WHEN Active=1 THEN 0 ELSE Active END
FROM
<Tables>
WHERE
<JOIN Conditions>

How would I write this SQL query?

I have the following tables:
PERSON_T DISEASE_T DRUG_T
========= ========== ========
PERSON_ID DISEASE_ID DRUG_ID
GENDER PERSON_ID PERSON_ID
NAME DISEASE_START_DATE DRUG_START_DATE
DISEASE_END_DATE DRUG_END_DATE
I want to write a query that takes an input of a disease id and returns one row for each person in the database with a column for the gender, a column for whether or not they have ever had the disease, and a column for each drug which specifies if they took the drug before contracting the disease. I.E. true would mean drug_start_date < disease_start_date. False would mean drug_start_date>disease_start_date or the person never took that particular drug.
We currently pull all of the data from the database and use Java to create a 2D array with all of these values. We are investigating moving this logic into the database. Is it possible to create a query that will return the result set as I want it or would I have to create a stored procedure? We are using Postgres, but I assume an SQL answer for another database will easily translate to Postgres.
Based on the info provided:
SELECT p.name,
p.gender,
CASE WHEN d.disease_id IS NULL THEN 'N' ELSE 'Y' END AS had_disease,
dt.drug_id
FROM PERSON p
LEFT JOIN DISEASE d ON d.person_id = p.person_id
AND d.disease_id = ?
LEFT JOIN DRUG_T dt ON dt.person_id = p.person_id
AND dt.drug_start_date < d.disease_start_date
..but there's going to be a lot of rows that will look duplicate except for the drug_id column.
You're essentially looking to create a cross-tab query with the drugs. While there are plenty of OLAP tools out there that can do this sort of thing (among all sorts of other slicing and dicing of the data), doing something like this in traditional SQL is not easy (and, in general, impossible to do without some sort of procedural syntax in all but the simplest scenarios).
You essentially have two options when doing this with SQL (well, more accurately, you have one option, and another more complicated but flexible option that derives from it):
Use a series of CASE statements in your query to produce columns that are representative of each individual drug. This requires knowing the list of variable values (i.e. drugs) ahead of time
Use a procedural SQL language, such as T-SQL, to dynamically construct a query that uses case statements as described above, but along with obtaining that list of values from the data itself.
The two options essentially do the same thing, you're just trading simplicity and ease of maintenance for flexibility in the second option.
For example, using option 1:
select
p.NAME,
p.GENDER,
(case when d.DISEASE_ID is null then 0 else 1 end) as HAD_DISEASE,
(case when sum(case when dr.DRUG_ID = 1 then 1 else 0 end) > 0 then 1 else 0 end) as TOOK_DRUG_1,
(case when sum(case when dr.DRUG_ID = 2 then 1 else 0 end) > 0 then 1 else 0 end) as TOOK_DRUG_2,
(case when sum(case when dr.DRUG_ID = 3 then 1 else 0 end) > 0 then 1 else 0 end) as TOOK_DRUG_3
from PERSON_T p
left join DISEASE_T d on d.PERSON_ID = p.PERSON_ID and d.DISEASE_ID = #DiseaseId
left join DRUG_T dr on dr.PERSON_ID = p.PERSON_ID and dr.DRUG_START_DATE < d.DISEASE_START_DATE
group by p.PERSON_ID, p.NAME, p.GENDER, d.DISEASE_ID
As you can tell, this gets a little laborious as you get outside of just a few potential values.
The other option is to construct this query dynamically. I don't know PostgreSQL and what, if any, procedural capabilities it has, but the overall procedure would be this:
Gather list of potential DRUG_ID values along with names for the columns
Prepare three string values: the SQL prefix (everything before the first drug-related CASE statement, the SQL stuffix (everything after the last drug-related CASE statement), and the dynamic portion
Construct the dynamic portion by combining drug CASE statements based upon the previously retrieved list
Combine them into a single (hopefully valid) SQL statement and execute