SQL count on a field which is a code - sql

I have a table
ANSWERS
qId
toggle_value
which records an HTML radio button value {Yes, N/A, No, Resolved}
Now I want to count and summarize how many yeses, nos, nas and resolved grouped by question Id.
For simplicity I started to build each individual query.
SELECT qId, count(*) as yes_qty FROM ANSWERS WHERE TOGGLE_VALUE='Yes' GROUP BY qId;
SELECT qId, count(*) as na_qty FROM ANSWERS WHERE TOGGLE_VALUE='NA' GROUP BY qId;
SELECT qId, count(*) as no_qty FROM ANSWERS WHERE TOGGLE_VALUE='No' GROUP BY qId;
SELECT qId, count(*) as resolved_qty FROM ANSWERS WHERE TOGGLE_VALUE='Resolved' GROUP BY qId;
But I really want it in one query so I can iterate over the list and display something like this ( aggregating 14 Checklists with 3 questions)
Q Yes No NA Resolved
1 4 10 0 10
2 14 0 0 0
3 7 0 7 0
I don't actually use strings for the toggle value but numbers 1=yes, 2=NA, etc... and was wondering if a better table design would have been
ANSWERS
qId
yes_value
no_value
na_value
resolved_value
I'd have to refactor a lot of other things if I changed the table deisgn so I was hoping to get a single query working.

SELECT qId,
SUM(CASE WHEN TOGGLE_VALUE='Yes' THEN 1 ELSE 0 END) AS YesQty,
SUM(CASE WHEN TOGGLE_VALUE='No' THEN 1 ELSE 0 END) AS NoQty,
SUM(CASE WHEN TOGGLE_VALUE='NA' THEN 1 ELSE 0 END) AS NAQty,
SUM(CASE WHEN TOGGLE_VALUE='Resolved' THEN 1 ELSE 0 END) AS ResolvedQty
FROM ANSWERS
GROUP BY qId

I like your table design. I would personally solve this with a SUM and GROUP BY and using CASE clauses to create new columns.

Related

SQL/PL Rows to Columns using max query with Sub-query

I was using max function to convert from rows to column and before using it in sub-query, it works well.
Situation: [Please refer to the picture as hyperlinked] I have a total of three questions for customer to answer and their responses will be extracted from the database. However, for the first question, customer is allowed to choose from 1 - 10. 10 refers to free text and will be stored in answer for Question = 2.
However, I would like to exclude free text input from the customer and for the extraction to be in column. Having to say that I will be having three columns: Response_1, Response_2 and Response_3. When customer choose 10 for Question = 1, the answer for Question = 3 will be stored in Response_2 while answer for Question = 4 in Response_3.
My attempt is as follow:
select customer_ID
max( CASE WHEN Question = 1 THEN Answer END) Response_1,
max( CASE WHEN Question = 1 AND Answer != 10 THEN
( select
max( CASE WHEN Question = 2 THEN Answer END)
from t_question_answer)
ELSE
( select
max( CASE WHEN Question = 3 THEN Answer END)
from t_question_answer)
END)
) Response_2
from t_question_answer
group by customer_ID
The result went wrong when it comes to the data extracted for customer_2 where I think in the sub-query, it looks for the maximum value in the whole data again instead of specifying the same customer.
You need more conditional logic in your conditional aggregation:
select customer_ID
max(CASE WHEN Question = 1 THEN Answer END) Response_1,
(case when max(case when question = 1 and answer = 10 then 1 else 0 end) > 0
then max( CASE WHEN Question = 3 THEN Answer END)
else max( CASE WHEN Question = 2 THEN Answer END)
end) as Response_2,
(case when max(case when question = 1 and answer = 10 then 1 else 0 end) > 0
then max( CASE WHEN Question = 4 THEN Answer END)
else max( CASE WHEN Question = 3 THEN Answer END)
end) as Response_3
from t_question_answer
group by customer_ID

Create a query that counts instances in a related table

I have re-written this query about 20 times today and I keep getting close but no dice... I'm sure this is easy-peasy for y'all, but my SQL (Oracle) is pretty rusty.
Here's what I need:
PersonID Count1 Count2 Count3 Count4
1 0 0 2 1
2 1 1 1 0
3 1 1 1 2
Data is coming from several sources. I have a table People, and a table Values. People can have any number of values in that table.
PersonID Item Value
1 Check1 3
1 Check2 3
1 Check3 4
2 Check4 2
2 Check5 3
2 Check6 1
.. etc
So the query would, for each PersonID, count how many times the particular Value appears. The values are always 1, 2, 3, or 4. I tried to do 4 subqueries, but it wouldn't read the PersonID from the main query and just returned the count of all instances of value=1.
I was then thinking do a Group_By ... I don't know. Any help is appreciated!
ETA: I've deleted & re-written the query many times in many ways and unfortunately did not save any intermediate attempts. I didn't include it originally because I was in the middle of rearranging it again, and it's not runnable as-is. But here it is as it stands now:
/*sources are the tested requirements
values are the scores people received on the tested sources
people are those who were tested on the requirements */
WITH sub_query4 (
SELECT values.personid,
count (values.ID) as count4 --how many 4s
FROM values
INNER JOIN sources ON values.valueid = sources.sourceid
INNER JOIN people ON people.personid = values.personid
WHERE values.yearid = 2017
AND values.quarter = 'Q1'
AND instr (sources.identifier, 'TESTBANK.01', 1 ,1) > 0
AND values.value = '4'
GROUP_BY people.personid
)
SELECT p.first_name,
p.last_name,
p.position,
p.email,
p.locationid,
sub_query4.count4 as count4 --eventually this would repeat for 1, 2, & 3
FROM people p
WHERE p.locationid=406
AND p.position in (9,10);
values is a bad name for a table because it is a SQL keyword.
In any case, conditional aggregation should work:
select personid,
sum(case when value = 1 then 1 else 0 end) as cnt_1,
sum(case when value = 2 then 1 else 0 end) as cnt_2,
sum(case when value = 3 then 1 else 0 end) as cnt_3,
sum(case when value = 4 then 1 else 0 end) as cnt_4
from values
group by personid;
I prefer to use PIVOT for this. Here is Example SQL Fiddle
SELECT "PersonID", val1,val2,val3,val4 FROM
(
SELECT "PersonID", "Value" from VALS
)
PIVOT
(
count("Value")
FOR "Value" IN (1 as val1, 2 as val2, 3 as val3, 4 as val4)
);

best query for a complex situation

I have a table with following columns,(id,fkid, flag1,flag2,flag3,flag4). The possible value for each flag field is -1 to 3 and null is allowed. What I need is a query to check if count of any flag field with value 2 is greater than 3 for a given foreign key fkid. The way I am doing is write a query for each field. It works but very not smart to me. Anyone has better idea? Thanks.
You can do this with one query:
select fkid
from t
group by fkid
having sum(case when flag1 = 2 then 1 else 0 end) > 3 or
sum(case when flag2 = 2 then 1 else 0 end) > 3 or
sum(case when flag3 = 2 then 1 else 0 end) > 3 or
sum(case when flag4 = 2 then 1 else 0 end) > 3
I do agree strongly with the comments, though, that sample data, sample results, and a clear table structure would greatly improve the question.
Query below will also answer your question, sql fiddle example: http://sqlfiddle.com/#!3/adda9/2
SELECT
DISTINCT fkid
FROM
tblTest pvt
UNPIVOT
(
FlagValue FOR Flag IN
(flag1,flag2,flag3,flag4)
) as Unpvt
WHERE
FlagValue = 2
GROUP BY
FKID, FLAG
HAVING COUNT(*)>3

SQL Count with multiple conditions then join

Quick one,
I have a table, with the following structure
id lid taken
1 1 0
1 1 0
1 1 1
1 1 1
1 2 1
Pretty simply so far right?
I need to query the taken/available from the lid of 1, which should return
taken available
2 2
I know I can simply do two counts and join them, but is there a more proficient way of doing this rather than two separate queries?
I was looking at the following type of format, but I can not for the life of me get it executed in SQL...
SELECT
COUNT(case taken=1) AS taken,
COUNT(case taken=0) AS available FROM table
WHERE
lid=1
Thank you SO much.
You can do this:
SELECT taken, COUNT(*) AS count
FROM table
WHERE lid = 1
GROUP BY taken
This will return two rows:
taken count
0 2
1 2
Each count corresponds to how many times that particular taken value was seen.
Your query is correct just needs juggling a bit:
SELECT
SUM(case taken WHEN 1 THEN 1 ELSE 0 END) AS taken,
SUM(case taken WHEN 1 THEN 0 ELSE 1 END) AS available FROM table
WHERE
lid=1
Alternatively you could do:
SELECT
SUM(taken) AS taken,
COUNT(id) - SUM(taken) AS available
FROM table
WHERE
lid=1
SELECT
SUM(case WHEN taken=1 THEN 1 ELSE 0 END) AS taken,
SUM(case WHEN taken=0 THEN 1 ELSE 0 END) AS available
FROM table
WHERE lid=1
Weird application of CTE's:
WITH lid AS (
SELECT DISTINCT lid FROM taken
)
, tak AS (
SELECT lid,taken , COUNT(*) AS cnt
FROM taken t0
GROUP BY lid,taken
)
SELECT l.lid
, COALESCE(a0.cnt, 0) AS available
, COALESCE(a1.cnt, 0) AS taken
FROM lid l
LEFT JOIN tak a0 ON a0.lid=l.lid AND a0.taken = 0
LEFT JOIN tak a1 ON a1.lid=l.lid AND a1.taken = 1
WHERE l.lid=1
;

SQL COUNT - Output table with two COUNT columns with differing WHERE clauses

I have the need to generate a report in Oracle APEX that is similar to the example below:
PROJECT OPEN_ISSUE_COUNT CLOSED_ISSUE_COUNT
W-1 3 1
X-2 1 2
Y-3 5 3
Z-4 2 1
Where OPEN_ISSUE_COUNT and CLOSED_ISSUE_COUNT are generated by a SQL COUNT statement. The table being queried looks like this:
ISSUE_# ISSUE_STATUS ASSOCIATED_PROJECT
1A OPEN W-1
1B OPEN W-1
1C OPEN W-1
2A CLOSED W-1
2B OPEN X-2
2C CLOSED X-2
3A CLOSED X-2
etc...
So in one query I need to count for OPEN_ISSUE_COUNT and CLOSED_ISSUE_COUNT where ISSUS_STATUS = 'OPEN' and ISSUS_STATUS = 'CLOSED' respectively and GROUP BY ASSOCIATED_PROJECT.
Does that make sense? Obviously I can easily do this for one of the two statuses, but have been unable to come up with any viable solution for what I am describing here. I have looked over some stuff here and elsewhere online and did not see something similar. Let me know what you guys think. Thanks!
Since count() only counts non-null values, this should work:
select associated_project as project,
count(case when issue_status='OPEN' then 1 else null end) as open_issue_count,
count(case when issue_status='CLOSED' then 1 else null end) as closed_issue_count
from table
group by associated_project;
Of course, this assumes that the only valid values for issue_status are 'OPEN' AND 'CLOSED'. If this isn't the case--and if you want those other statuses counted--then adjust the query accordingly.
Another way to do it is with the new PIVOT feature:
with issue_data as (
select associated_project as project, issue_status from issues
)
select project, open, closed
from issue_data
pivot ( count(*) for issue_status in ('OPEN' as open, 'CLOSED' as closed) )
select
sum(case when status = 'open' then 1 else 0 end) as open,
sum(case when status = 'closed' then 1 else 0 end) as closed
from table
where <other restrictions>