CASE expression from multiple tables - sql

I'm trying to write a CASE expression where I look at 2 tables (same DB) to output a 1 or 0 and then SUM that column with some grouping on BRANCH and EVUSERID.
This may be the wrong method altogether for what I'm wanting to achieve so am completely open to suggestions.
EVENT table
+--------+----------+-----------+---------------------------+-----------+
| BRANCH | EVUSERID | EVENTTYPE | REFERENC | ENQUIRYNO |
+--------+----------+-----------+---------------------------+-----------+
| 1 | cunnc | SAS | New Cust - Unknown Source | 1468731 |
| 2 | malce | SAS | New Cust - Walk-in | 1792469 |
| 1 | cunnc | SAS | Old Cust - Retention | 897324 |
| 4 | wardt | OPT | | 986234 |
| 5 | shera | OPT | | 576324 |
| 4 | hamzk | SAS | Old Cust - Service | 1238746 |
| 5 | chinc | SAS | New Cust - Digital | 687236 |
+--------+----------+-----------+---------------------------+-----------+
SORDER table
+-----------+---------+
| ENQUIRYNO | NEWUSED |
+-----------+---------+
| 1468731 | N |
| 1792469 | N |
| 897324 | U |
| 986234 | N |
| 576324 | U |
| 1238746 | U |
| 687236 | U |
+-----------+---------+
My desired output would be
+--------+----------+----------------+---------+-------+---------+-----------+---------+-------------+--------------+---------+
| BRANCH | EVUSERID | Unknown Source | Walk-in | Phone | Digital | Retention | Service | New Enquiry | Used Enquiry | Unknown |
+--------+----------+----------------+---------+-------+---------+-----------+---------+-------------+--------------+---------+
| 1 | cunnc | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
| 2 | malce | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| 4 | hamzk | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
| 5 | chinc | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
+--------+----------+----------------+---------+-------+---------+-----------+---------+-------------+--------------+---------+
Where I am so far. All is working as expected until I added my join in
SELECT
E.CM_EV_BRANCH,
E.CM_EV_EVUSERID,
SUM(CASE WHEN E.CM_EV_EVENTTYPE = 'SAS' AND E.CM_EV_REFERENC LIKE '%Unknown%' THEN 1 END) AS 'UnknownSource',
SUM(CASE WHEN E.CM_EV_EVENTTYPE = 'SAS' AND E.CM_EV_REFERENC LIKE '%Walk%' THEN 1 END) AS 'Walk-in',
SUM(CASE WHEN E.CM_EV_EVENTTYPE = 'SAS' AND E.CM_EV_REFERENC LIKE '%Phone%' THEN 1 END) AS 'Phone',
SUM(CASE WHEN E.CM_EV_EVENTTYPE = 'SAS' AND E.CM_EV_REFERENC LIKE '%Digital%' THEN 1 END) AS 'Digital',
SUM(CASE WHEN E.CM_EV_EVENTTYPE = 'SAS' AND E.CM_EV_REFERENC LIKE '%Retention%' THEN 1 END) AS 'Retention',
SUM(CASE WHEN E.CM_EV_EVENTTYPE = 'SAS' AND E.CM_EV_REFERENC LIKE '%Service%' THEN 1 END) AS 'Service',
SUM(CASE WHEN E.CM_EV_EVENTTYPE = 'SAS' AND S.VM_SO_NEWUSED = 'N' THEN 1 END) AS 'New Enquiry',
SUM(CASE WHEN E.CM_EV_EVENTTYPE = 'SAS' AND S.VM_SO_NEWUSED = 'U' THEN 1 END) AS 'Used Enquiry',
SUM(CASE WHEN E.CM_EV_EVENTTYPE = 'SAS' AND S.VM_SO_NEWUSED <> 'U' AND S.VM_SO_NEWUSED <> 'N' THEN 1 END) AS 'Unknown'
FROM
Datalake.dbo.CM_01_EVENT E
LEFT JOIN Datalake.dbo.VM_01_SORDER S ON E.CM_EV_ENQUIRYNO = S.VM_SO_ENQUIRYNO
WHERE
E.CM_EV_BRANCH <> 0 AND E.CM_EV_DEPTTYPE = 'S'
GROUP BY
E.CM_EV_BRANCH, E.CM_EV_EVUSERID
/*
HAVING SUM(CASE WHEN E.CM_EV_EVENTTYPE = 'SAS' AND E.CM_EV_REFERENC LIKE '%Unknown%' OR E.CM_EV_REFERENC LIKE '%Walk%' OR E.CM_EV_REFERENC LIKE '%Phone%' OR E.CM_EV_REFERENC LIKE '%Digital%' THEN 1 ELSE 0 END) <> 0
*/
ORDER BY
E.CM_EV_BRANCH, E.CM_EV_EVUSERID
I've commented out the HAVING clause as that didn't work as desired. There are instances in the DB where an EVUSERID exists for a given branch but doesn't meet the criteria in the CASE expressions. These would display as "0" which whilst is correct, I wasn't interested in seeing during developing the SQL statement.
Any help would be gratefully appreciated.

I think you want a where clause: this filters out rows that would have all 0s, and lets you simplify the case expressions in the subquery (since one condition is common to all expressions).
Also: don't use single quotes for column aliases! They are meant for literal strings. Use aliases that do not require quoting - or use square brackets to quote them.
So:
SELECT
E.CM_EV_BRANCH,
E.CM_EV_EVUSERID,
SUM(CASE WHEN E.CM_EV_REFERENC LIKE '%Unknown%' THEN 1 ELSE 0 END) AS UnknownSource,
SUM(CASE WHEN E.CM_EV_REFERENC LIKE '%Walk%' THEN 1 ELSE 0 END) AS Walk_in,
SUM(CASE WHEN E.CM_EV_REFERENC LIKE '%Phone%' THEN 1 ELSE 0 END) AS Phone,
SUM(CASE WHEN E.CM_EV_REFERENC LIKE '%Digital%' THEN 1 ELSE 0 END) AS Digital,
SUM(CASE WHEN E.CM_EV_REFERENC LIKE '%Retention%' THEN 1 ELSE 0 END) AS Retention,
SUM(CASE WHEN E.CM_EV_REFERENC LIKE '%Service%' THEN 1 ELSE 0 END) AS Service,
SUM(CASE WHEN S.VM_SO_NEWUSED = 'N' THEN 1 ELSE 0 END) AS NewEnquiry,
SUM(CASE WHEN S.VM_SO_NEWUSED = 'U' THEN 1 ELSE 0 END) AS Used Enquiry,
SUM(S.VM_SO_NEWUSED NOT IN ('U', 'N') THEN 1 ELSE 0 END) AS Unknown
FROMDatalake.dbo.CM_01_EVENT E
LEFT JOIN Datalake.dbo.VM_01_SORDER S ON E.CM_EV_ENQUIRYNO = S.VM_SO_ENQUIRYNO
WHERE
E.CM_EV_BRANCH <> 0
AND E.CM_EV_DEPTTYPE = 'S'
AND E.CM_EV_EVENTTYPE = 'SAS'
AND (
E.CM_EV_REFERENC LIKE '%Unknown%'
OR E.CM_EV_REFERENC LIKE '%Walk%'
OR E.CM_EV_REFERENC LIKE '%Phone%'
OR E.CM_EV_REFERENC LIKE '%Digital%'
OR E.CM_EV_REFERENC LIKE '%Retention%'
OR E.CM_EV_REFERENC LIKE '%Service%'
)
GROUP BY E.CM_EV_BRANCH, E.CM_EV_EVUSERID
ORDER BY E.CM_EV_BRANCH, E.CM_EV_EVUSERID

Related

How to know arithmetic mean of two count values

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.

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: query one column in same table

A football manager here. How do I:
Select all matches that have kicked-off but never had a goal.
Select all matches that kicked-off more than 1h ago but haven't yet had a goal or a corner-kick.
| Match | Event | EventTime |
|-------------------------------------------|
| 1 | Kick-off | 2014-12-15T16:00:00 |
| 1 | Throw-in | 2014-12-15T16:15:00 |
| 1 | Goal | 2014-12-15T16:20:00 |
| 1 | Corner-kick | 2014-12-15T16:30:00 |
| 1 | End | 2014-12-15T17:30:00 |
| 2 | Kick-off | 2014-12-10T16:00:00 |
| 2 | Goal | 2014-12-10T16:01:00 |
| 3 | Kick-off | 2014-12-05T08:00:00 |
| 3 | Corner-kick | 2014-12-05T08:10:00 |
I feel this should be simple, but I'm stuck somehow.
1:
SELECT DISTINCT Match
FROM dbo.YourTable A
WHERE [Event] = 'Kick-off'
AND NOT EXISTS( SELECT 1 FROM dbo.YourTable
WHERE Match = A.Match
AND [Event] = 'Goal')
2:
SELECT DISTINCT Match
FROM dbo.YourTable A
WHERE [Event] = 'Kick-off'
AND EventTime <= GETDATE(HOUR,-1,GETDATE())
AND NOT EXISTS( SELECT 1 FROM dbo.YourTable
WHERE Match = A.Match
AND [Event] IN ('Goal','Corner-kick'))
You would do this with aggregation and a having clause. For the first:
select match
from table t
group by match
having sum(case when event = 'Kick-off' then 1 else 0 end) > 0 and
sum(case when event = 'Goal' then 1 else 0 end) = 0;
For the second:
select match
from table t
group by match
having max(sum case when event = 'Kick-off' then eventtime end) <= getdate() - 1.0/24 and
sum(case when event in ('Goal', 'Corner-Kick') then 1 else 0 end) = 0;
Each condition in the having clause counts the number of rows that match the condition. > 0 means that at least one row matched. = 0 means no rows match.

Combine multiple lines onto one line

I have the following query
SELECT orderno,
CASE WHEN param_id = 'variant' THEN param_val END AS 'variant',
CASE WHEN param_id = 'period_from' THEN param_val END AS'period_from',
CASE WHEN param_id = 'period_to' THEN param_val END AS'period_to',
CASE WHEN param_id = 'division' THEN param_val END AS 'division',
CASE WHEN param_id = 'show_div' THEN param_val END AS 'show_div',
CASE WHEN param_id = 'group_div' THEN param_val END AS 'group_div',
FROM orderreport
order by orderno
This returns a grid similar to the below (there are another number of columns which I have removed for the purpose of the question) There is also an infinite number of order nos
orderno | variant | period_from | period_to | division | show_div | group_div
3 | AH003 | NULL | NULL | NULL | NULL | NULL
3 | NULL | 201300 | NULL | NULL | NULL | NULL
3 | NULL | NULL | 201304 | NULL | NULL | NULL
3 | NULL | NULL | NULL | SALES | NULL | NULL
3 | NULL | NULL | NULL | NULL | Y | NULL
3 | NULL | NULL | NULL | NULL | NULL | Y
My desired output is as the below, no matter what i have tried I am stumped.
orderno | variant | period_from | period_to | division | show_div | group_div
3| AH003 | 201300 | 201304 | SALES | Y | Y
Your query is very close, I would add an aggregate function around your CASE expressions and then a GROUP BY:
SELECT orderno,
max(CASE WHEN param_id = 'variant' THEN param_val END) AS variant,
max(CASE WHEN param_id = 'period_from' THEN param_val END) AS period_from,
max(CASE WHEN param_id = 'period_to' THEN param_val END) AS period_to,
max(CASE WHEN param_id = 'division' THEN param_val END) AS division,
max(CASE WHEN param_id = 'show_div' THEN param_val END) AS show_div,
max(CASE WHEN param_id = 'group_div' THEN param_val END) AS group_div
FROM orderreport
group by orderno
order by orderno