How to know arithmetic mean of two count values - sql

I have table answers where I store information.
| EMPLOYEE | QUESTION_ID | QUESTION_TEXT | SELECTED_OPTION_ID | SELECTED_OPTION_TEXT |
|----------|-------------|------------------------|--------------------|----------------------|
| Mark | 1 | Do you like soup? | 1 | Yes |
| Kate | 1 | Do you like soup? | 1 | Yes |
| Jone | 1 | Do you like soup? | 2 | No |
| Kim | 1 | Do you like soup? | 3 | I don't know |
| Alex | 1 | Do you like soup? | 2 | No |
| Bond | 1 | Do you like soup? | 1 | Yes |
| Ford | 1 | Do you like soup? | 3 | I don't know |
| Mark | 2 | Do you like ice cream? | 2 | No |
| Kate | 2 | Do you like ice cream? | 1 | Yes |
| Jone | 2 | Do you like ice cream? | 1 | Yes |
| Kim | 2 | Do you like ice cream? | 1 | Yes |
| Alex | 2 | Do you like ice cream? | 2 | No |
| Bond | 2 | Do you like ice cream? | 1 | Yes |
| Ford | 2 | Do you like ice cream? | 3 | I don't know |
Formulas:
value_1 = (Number of users who answered "No" or "I don't know" to the first question) / (The total number of people who answered to the first question)
value_2 = (Number of users who answered "No" or "I don't know" to the second question) / (The total number of people who answered to the first question)
I can separately find the values according to the above formulas. For example value_1:
select
count(*)
from
answers
where
question_id = 1
and (
selected_option_id in (2, 3)
or
selected_option_text in ('No', 'I don\'t know')
)
My question is how to arithmetic mean of these 2 values correctly by one sql query?
In other words I need to find average value:

You could use a condition sum
select (sum( case when QUESTION_ID = 1 AND
SELECTED_OPTION_ID in ( 2,3) THEN 1 else 0 end )::float /
sum( case when QUESTION_ID = 1 then 1 else 0 end)::float )*100 first_question_rate,
(sum( case when QUESTION_ID = 2 AND
SELECTED_OPTION_ID in ( 2,3) THEN 1 else 0 end )::float /
sum( case when QUESTION_ID = 2 then 1 else 0 end)::float)*100 second_question_rate,
(( sum( case when QUESTION_ID = 1 AND SELECTED_OPTION_ID in ( 2,3) THEN 1 else 0 end )::float /
sum( case when QUESTION_ID = 1 then 1 else 0 end)::float +
sum( case when QUESTION_ID = 2 AND SELECTED_OPTION_ID in ( 2,3) THEN 1 else 0 end )::float /
sum( case when QUESTION_ID = 2 then 1 else 0 end) ::float)/2)*100 avg
from answer

Are you looking for something like below-
SELECT
SUM(CASE WHEN QUESTION_ID = 1 AND SELECTED_OPTION_TEXT <> 'Yes' THEN 1 ELSE 0 END)
/
SUM(CASE WHEN QUESTION_ID = 1 THEN 1 ELSE 0 END) value_1 ,
SUM(CASE WHEN QUESTION_ID = 2 AND SELECTED_OPTION_TEXT <> 'Yes' THEN 1 ELSE 0 END)
/
SUM(CASE WHEN QUESTION_ID = 2 THEN 1 ELSE 0 END) value_2
FROM answers
For getting average, please use the below script-
SELECT (A.value_1+A.value_2)/2.0
FROM
(
SELECT
SUM(CASE WHEN QUESTION_ID = 1 AND SELECTED_OPTION_TEXT <> 'Yes' THEN 1 ELSE 0 END)*1.0
/
SUM(CASE WHEN QUESTION_ID = 1 THEN 1 ELSE 0 END)*1.0 value_1 ,
SUM(CASE WHEN QUESTION_ID = 2 AND SELECTED_OPTION_TEXT <> 'Yes' THEN 1 ELSE 0 END)*1.0
/
SUM(CASE WHEN QUESTION_ID = 2 THEN 1 ELSE 0 END)*1.0 value_2
FROM answers
)A

I'm pretty sure you want conditional aggregation. I suspect you want:
select question_id,
count(*) filter (where selected_option_id in (2, 3)) as num_2_3,
avg( selected_option_id in (2, 3)::int ) as ratio_2_3
from answers
group by question_id;
For each question, this provides the number of answers that are 2 or 3 and the ratio of those answers to all answers.

Related

Select specific columns by their alias, and later order by it

My table are the following
+----+----------+--------+
| id | priority | User |
+----+----------+--------+
| 1 | 2 | [null] |
| 2 | 1 | [null] |
| 3 | 3 | Tony |
| 4 | 2 | John |
| 5 | 2 | Andy |
| 6 | 1 | Mike |
+----+----------+--------+
My goal is to extract them, and order by the following combined conditions:
priority = 1
User is null
+----+----------+--------+-----------+
| id | priority | User | peak_rows |
+----+----------+--------+-----------+
| 1 | 2 | [null] | 1 |
| 2 | 1 | [null] | 1 |
| 6 | 1 | Mike | 0 |
| 3 | 3 | Tony | 1 |
| 4 | 2 | John | 0 |
| 5 | 2 | Andy | 0 |
+----+----------+--------+-----------+
This is what I guess I can do
select
id,
CASE WHEN priority = 1 THEN 1 ELSE 0 END as c1,
CASE WHEN User is NULL THEN 1 ELSE 0 END as c2,
c1 + c2 AS peak_rows
FROM mytable
ORDER BY peak_rows DESC
but it cause an error:
ERROR: column "c1" does not exist
LINE 5: c1+ c2as pp
^
SQL state: 42703
Character: 129
I don't know why I make 2 columns(c1 and c2), but I can not use it later.
Any good idea to do that?
You are not making two columns and using them later, you are making them and want to use them at the same time. You could use a subquery.
SELECT a.id, a.priority, a.User, a.c1 + a.c2 AS peak_rows
FROM
(SELECT id,
priority,
User,
CASE WHEN priority = 1 THEN 1 ELSE 0 END as c1,
CASE WHEN User IS NULL THEN 1 ELSE 0 END as c2,
FROM mytable) a
ORDER BY peak_rows DESC;
select
id,
CASE WHEN priority = 1 THEN 1 ELSE 0 END as c1,
CASE WHEN User is NULL THEN 1 ELSE 0 END as c2,
(CASE WHEN priority = 1 THEN 1 ELSE 0) + ( CASE WHEN User is NULL THEN 1 ELSE 0 END) AS peak_rows
FROM mytable
ORDER BY peak_rows DESC
I suppose your aim is to order by those c1 and c2, so you can directly use in the order by clause. You just need to interchange 0 and 1 in the case..when statements. And depending on your priority=1 criteria id=2 must stay at the top.
with mytable( id, priority, "User" ) as
(
select 1 , 2, null union all
select 2, 1, null union all
select 3, 3, 'Tony' union all
select 4, 2, 'John' union all
select 5, 2, 'Andy' union all
select 6, 1, 'Mike'
)
select *
from mytable
order by ( case when priority = 1 then 0 else 1 end ) +
( case when "User" is null then 0 else 1 end );
id priority User
-- -------- -------
2 1 [null]
1 2 [null]
6 1 Mike
3 3 Tony
4 2 John
5 2 Andy
Demo

One SQL query with multiple conditions

I am running an Oracle database and have two tables below.
#account
+----------------------------------+
| acc_id | date | acc_type |
+--------+------------+------------+
| 1 | 11-07-2018 | customer |
| 2 | 01-11-2018 | customer |
| 3 | 02-09-2018 | employee |
| 4 | 01-09-2018 | customer |
+--------+------------+------------+
#credit_request
+-----------------------------------------------------------------+
| credit_id | date | credit_type | acc_id | credit_amount |
+------------+-------------+---------- +--------+
| 1112 | 01-08-2018 | failed | 1 | 2200 |
| 1214 | 02-12-2018 | success | 2 | 1500 |
| 1312 | 03-11-2018 | success | 4 | 8750 |
| 1468 | 01-12-2018 | failed | 2 | 3500 |
+------------+-------------+-------------+--------+---------------+
Want to have followings for each customer:
the last successful credit_request
sum of credit_amount of all failed credit_requests
Here is one method:
select a.acct_id, acr.num_fails,
acr.num_successes / nullif(acr.num_fails) as ratio, -- seems weird. Why not just the failure rate?
last_cr.credit_id, last_cr.date, last_cr.credit_amount
from account a left join
(select acc_id,
sum(case when credit_type = 'failed' then 1 else 0 end) as num_fails,
sum(case when credit_type = 'failed' then credit_amount else 0 end) as num_fails,
sum(case when credit_type = 'success' then 1 else 0 end) as num_successes
max(case when credit_type = 'success' then date else 0 end) as max_success_date
from credit_request
group by acct_id
) acr left join
credit_request last_cr
on last_cr.acct_id = acr.acct_id and last_cr.date = acr.date;
The following query should do the trick.
SELECT
acc_id,
MAX(CASE WHEN credit_type = 'success' AND rn = 1 THEN credit_id END) as last_successfull_credit_id,
MAX(CASE WHEN credit_type = 'success' AND rn = 1 THEN cdate END) as last_successfull_credit_date,
MAX(CASE WHEN credit_type = 'success' AND rn = 1 THEN credit_amount END) as last_successfull_credit_amount,
SUM(CASE WHEN credit_type = 'failed' THEN credit_amount ELSE 0 END) total_amount_of_failed_credit,
SUM(CASE WHEN credit_type = 'failed' THEN 1 ELSE 0 END) / COUNT(*) ratio_success_request
FROM (
SELECT
a.acc_id,
a.cdate adate,
a.acc_type,
c.credit_id,
c.cdate,
c.credit_type,
c.credit_amount,
ROW_NUMBER() OVER(PARTITION BY c.acc_id, c.credit_type ORDER BY c.cdate DESC) rn
FROM
account a
LEFT JOIN credit_request c ON c.acc_id = a.acc_id
) x
GROUP BY acc_id
ORDER BY acc_id
The subquery assigns a sequence to each record, within groups of accounts and credit types, using ROW_NUMBR(). The outer query does conditional aggrgation to compute the different computation you asked for.
This Db Fiddle demo with your test data returns :
ACC_ID | LAST_SUCCESSFULL_CREDIT_ID | LAST_SUCCESSFULL_CREDIT_DATE | LAST_SUCCESSFULL_CREDIT_AMOUNT | TOTAL_AMOUNT_OF_FAILED_CREDIT | RATIO_SUCCESS_REQUEST
-----: | -------------------------: | :--------------------------- | -----------------------------: | ----------------------------: | --------------------:
1 | null | null | null | 2200 | 1
2 | 1214 | 02-DEC-18 | 1500 | 3500 | .5
3 | null | null | null | 0 | 0
4 | 1312 | 03-NOV-18 | 8750 | 0 | 0
This might be what you are looking for... Since you did not show expected results, this might not be 100% accurate, feel free to adapt this.
I guess the below query is easy to understand and implement. Also, to avoid more and more terms in the CASE statements you can just make use of WITH clause and use it in the CASE statements to reduce the query size.
SELECT a.acc_id,
c.credit_type,
(distinct c.credit_id),
CASE WHEN
c.credit_type='success'
THEN max(date)
END CASE,
CASE WHEN
c.credit_type='failure'
THEN sum(credit_amount)
END CASE,
(CASE WHEN
c.credit_type='success'
THEN count(*)
END CASE )/
( CASE WHEN
c.credit_type='failure'
THEN count(*)
END CASE)
from accounts a LEFT JOIN
credit_request c on
a.acc_id=c.acc_id
where a.acc_type= 'customer'
group by c.credit_type

SQL query to compare records having count greater than 1

I have a table which contains id and answer columns which looks like:
| id | answer |
| 1 | Yes |
| 2 | No |
| 3 | Unsure |
| 1 | No |
| 1 | Unsure |
| 3 | Yes |
| 2 | Unsure |
| 4 | NULL |
| 4 | Unsure |
| 4 | No |
I want to the output in a way that if the userid has ever answered 'Yes' (like for example id 1) then the final output should have 1.
But if the userid has answered "No" and "NULL" then output should be "No"
Further if the userid has answered "Unsure" and "No" or "Unsure" and "Null" then output should be "Unsure"
Final Output:
| id | answer |
| 1 | Yes |
| 2 | Unsure |
| 3 | Yes |
| 4 | Unsure |
You want to use aggregation and case logic for this:
select id,
(case when sum(case when Answer = 'yes' then 1 else 0 end) > 0
then 'yes'
when sum(case when answer = 'unsure' then 1 else 0 end) > 0
then 'unsure'
else 'no' -- or perhaps max(answer)
end) as answer
from t
group by id;
If the only possibilities are "yes", "no", "unsure", and NULL, then you can take a short-cut:
select id,
(case when max(Answer) = 'yes'
then 'yes'
when max(answer) = 'unsure'
then 'unsure'
else 'no' -- or perhaps max(answer)
end) as answer
from t
group by id;
CASE WHEN (.Answer IS NULL OR .Answer = 'No') THEN 'NO' ELSE (.Answer) END AS 'FILTERED ANSWER'
Could This help you?

SQL - Select rows where all values in a column are the same

I have an SQL question I've been struggling with and hope someone can help. I have the following data:
TEAM | USERID | STEP1 | STEP2 | STEP3 | STEP4 | STEP5
001 | 000001 | Y | Y | N | N | Y
001 | 000002 | Y | N | N | Y | Y
002 | 000003 | N | Y | Y | N | N
002 | 000004 | N | Y | Y | Y | Y
003 | 000005 | Y | N | N | Y | N
003 | 000006 | Y | Y | Y | N | Y
What I need to do is return the value of the TEAM where all values of any STEPx are 'N'. So in the example above I would need TEAM 001 and 002 to be returned because in TEAM 001 all values of STEP3 are 'N', and in TEAM 002 all values of STEP1 are 'N'.
Any help would be much appreciated.
select team
from table
group by team
having
sum(case step1 when 'N' then 0 else 1 end) = 0
or sum(case step2 when 'N' then 0 else 1 end) = 0
or sum(case step3 when 'N' then 0 else 1 end) = 0
or sum(case step4 when 'N' then 0 else 1 end) = 0
or sum(case step5 when 'N' then 0 else 1 end) = 0
Here's a fiddle: http://sqlfiddle.com/#!6/ecbff/3
It may be better to normalize your data so that you have an int column named STEP and one row per team/user/step. This would make your query much less awkward.
I went the other way, but Blorgbeard was much faster than me.
select TEAM
from TEAMS
group by TEAM
having
count(case STEP1 when 'Y' then 1 end) = 0
or count(case STEP2 when 'Y' then 1 end) = 0
or count(case STEP3 when 'Y' then 1 end) = 0
or count(case STEP4 when 'Y' then 1 end) = 0
or count(case STEP5 when 'Y' then 1 end) = 0
There is no pure sql-based solution, but each conrete database may have its own way to iterate over all columns.
How ever, this approach is bad.
You should normalize your database.
|TEAM|USER_ID|STEP_ID|VALUE|
|1 | 1 | 1 | Y |
After it, it it will be easy to join team with this tables and filter all teams with "Y"

how to find out the duplicates and count them in another table

i have an sqlite table like Results and want to make a table like finalResults by counting the number of options. i am using sqlite
Table Name: Results Table Name: finalResults
___________ _________ __________ _______ ________ _______ _______
questionId| optionId questionId| option1 | option2 |option3 |option4
-------------------- -----------------------------------------------
40 | 3 40 | 1 | 2 | 4 | 1
40 | 3 41 | 2 | 1 | 1 | 3
40 | 2
40 | 4
40 | 2
40 | 1
40 | 3
40 | 3
41 | 1
41 | 3
41 | 2
41 | 4
41 | 4
41 | 4
41 | 1
Thank you
You can do it like this
INSERT INTO finalResults(questionId, option1, option2, option3, option4)
SELECT questionId,
MIN(CASE WHEN optionid = 1 THEN n END) option1,
MIN(CASE WHEN optionid = 2 THEN n END) option2,
MIN(CASE WHEN optionid = 3 THEN n END) option3,
MIN(CASE WHEN optionid = 4 THEN n END) option4
FROM
(
SELECT questionId, optionid, COUNT(*) n
FROM results
GROUP BY questionId, optionid
) q
GROUP BY questionId
Here is SQLFiddle demo
or
INSERT INTO finalResults(questionId, option1, option2, option3, option4)
SELECT questionId,
SUM(CASE WHEN optionid = 1 THEN 1 ELSE 0 END) option1,
SUM(CASE WHEN optionid = 2 THEN 1 ELSE 0 END) option2,
SUM(CASE WHEN optionid = 3 THEN 1 ELSE 0 END) option3,
SUM(CASE WHEN optionid = 4 THEN 1 ELSE 0 END) option4
FROM results
GROUP BY questionId
Here is SQLFiddle demo
Output in both cases:
| questionId | option1 | option2 | option3 | option4 |
------------------------------------------------------
| 40 | 1 | 2 | 4 | 1 |
| 41 | 2 | 1 | 1 | 3 |
try following query
select questionId,
sum(optionId==1) as option1,
sum(optionId==2) as option2,
sum(optionId==3) as option3
from results
group by questionId
or sqlfiddle http://sqlfiddle.com/#!7/415b7/6 (open into chrome & safari)