SQL statement using case, like, and having - sql

I am using an Oracle based system.
How do you use like, having, and a case statement together?
I am basically trying to list all of the unique individuals that are found in a transactional table that have more than 4 "Class A" transactions, or more than 1 "Class B" transactions. The reason why I want to use like is because the only way to diferentiate between transaction classes is by using a like statement in the transaction type column.
For example, there are many transaction types, but only "Class A" have '%ABC%' as part of their transaction type, and "Class B" are all the other types that do not have '%ABC%' in their transaction type column.
So again, I want my query to return only the indiv ids that have more than 4 "Class A" Transactions, or 1 "Class B" transaction.
here is what I have so far:
select tt.indiv_id, count(*) from transactiontable tt
group by tt.indiv_id
case when tt.tran_type like '%ABC'
having count(*) > 4
else
having count(*)>1.
I have searched a good bit on the site and I have not found an example using all of these functions together.

select tt.indiv_id,
count(case when tt.tran_type like '%ABC' then 1 end) as ClassACount,
count(case when tt.tran_type not like '%ABC' then 1 end) as ClassBCount
from transactiontable tt
group by tt.indiv_id
having count(case when tt.tran_type like '%ABC' then 1 end) > 4
or count(case when tt.tran_type not like '%ABC' then 1 end) > 1

Try this
select tt.indiv_id, count(*)
from transactiontable tt
group by tt.indiv_id, tt.tran_type
having count(*) > case when tt.tran_type like '%ABC' then 4 else 1 end

Your query is close. You want to keep track of each transaction type separately in the having clause:
select tt.indiv_id, count(*)
from transactiontable tt
group by tt.indiv_id
having sum(case when tt.tran_type like '%ABC%' then 1 else 0 end) > 4 or
sum(case when tt.tran_type not like '%ABC%' then 1 else 0 end) > 1

Related

How to merge two tables together given a specific criteria - Oracle SQL

I have a table that looks like this:
lob|desc|cnt
dogs,declined,5
cats,declined,5
rats,declined,8
dogs,failed,2
I am trying to create a new table that looks like this:
lob|cnt|failed_cnt
dogs,5,2
cats,5,0
rats,8,0
Essentially, I am merging results together by the column LOB so there is no duplicate records. LOB values that desc column value is failed, merge them together and take that cnt and put it in the new column ""failed_cnt". If that makes sense.
I wrote some SQL code that puts null values but there are stills duplicates. Here is my output:
lob|cnt|failed_cnt
dogs,5,0
cats,5,0
rats,8,0
dogs 0,2
Here is my code:
SELECT
lob,
CASE
WHEN CNT <> 0 AND desc not like 'Failed%'
THEN CNT
ELSE 0
END AS CNT,
CASE
WHEN CNT <> 0 AND desc LIKE 'Failed%'
THEN CNT
ELSE 0
END AS FAILED_CNT
FROM
table1
Any ideas or suggestions?
Use aggregation:
select lob,
sum(case when descr = 'declined' then cnt else 0 end) as declined,
sum(case when descr = 'failed' then cnt else 0 end) as failed
from t
group by lob;

SQL Case When Statement for Id with multiple rows

I have a table like so
Id Code
1 03J0
1 0304
1 03HI
2 033I
2 03J5
3 03J4
4 030H
I want to do a case when statement, when there is any occurrence where the Id has a Code that is like '%03J' then Happy otherwise Sad. How do I do this when an Id has multiple rows of different codes?
Intended output
Id Emotion
1 Happy
2 Happy
3 Happy
4 Sad
Is this what you want?
select id,
(case when sum(case when code like '03J%' then 1 else 0 end) > 0 then 'Happy' else 'Sad' end) as emotion
from t
group by id;
Using the ordering of strings, you can simplify this to:
select id,
min(case when code like '03J%' then 'Happy' else 'Sad' end) as emotion
from t
group by id;
Here is a db<>fiddle.
Using self-join. Judging from your sampple data, I think you want '03J%' instead of '%03J'
select distinct a.id, case when b.code is not null then 'Happy' else 'Sad' end as emotion
from mytable a
left join mytable b on a.id=b.id and b.code like '03J%';

Equivalent of string contains in google bigquery

I have a table like as shown below
I would like to create two new binary columns indicating whether the subject had steroids and aspirin. I am looking to implement this in Postgresql and google bigquery
I tried the below but it doesn't work
select subject_id
case when lower(drug) like ('%cortisol%','%cortisone%','%dexamethasone%')
then 1 else 0 end as steroids,
case when lower(drug) like ('%peptide%','%paracetamol%')
then 1 else 0 end as aspirin,
from db.Team01.Table_1
SELECT
db.Team01.Table_1.drug
FROM `table_1`,
UNNEST(table_1.drug) drug
WHERE REGEXP_CONTAINS( db.Team01.Table_1.drug,r'%cortisol%','%cortisone%','%dexamethasone%')
I expect my output to be like as shown below
Below is for BigQuery Standard SQL
#standardSQL
SELECT
subject_id,
SUM(CASE WHEN REGEXP_CONTAINS(LOWER(drug), r'cortisol|cortisone|dexamethasone') THEN 1 ELSE 0 END) AS steroids,
SUM(CASE WHEN REGEXP_CONTAINS(LOWER(drug), r'peptide|paracetamol') THEN 1 ELSE 0 END) AS aspirin
FROM `db.Team01.Table_1`
GROUP BY subject_id
if to apply to sample data from your question - result is
Row subject_id steroids aspirin
1 1 3 1
2 2 1 1
Note: instead of simple LIKE ending with lengthy and redundant text - I am using LIKE on steroids - which is REGEXP_CONTAINS
In Postgres, I would recommend using the filter clause:
select subject_id,
count(*) filter (where lower(drug) ~ 'cortisol|cortisone|dexamethasone') as steroids,
count(*) filter (where lower(drug) ~ 'peptide|paracetamol') as aspirin,
from db.Team01.Table_1
group by subject_id;
In BigQuery, I would recommend countif():
select subject_id,
countif(regexp_contains(drug, 'cortisol|cortisone|dexamethasone') as steroids,
countif(drug ~ ' 'peptide|paracetamol') as aspirin,
from db.Team01.Table_1
group by subject_id;
You can use sum(case when . . . end) as a more general approach. However, each database has a more "local" way of expressing this logic. By the way, the FILTER clause is standard SQL, just not widely adopted.
Use conditional aggregation. This is a solution that works across most (if not all) RDBMS:
SELECT
subject_id,
MAX(CASE WHEN drug IN ('cortisol', 'cortisone', 'dexamethasone') THEN 1 END) steroids,
MAX(CASE WHEN drug IN ('peptide', 'paracetamol') THEN 1 END) aspirin
FROM db.Team01.Table_1.drug
GROUP BY subject_id
NB: it is unclear why you are using LIKE, since it seems like you are having exact matches; I turned the LIKE condition to equalities.
you have missing group-by
select subject_id,
sum(case when lower(drug) in ('cortisol','cortisone','dexamethasone')
then 1 else 0 end) as steroids,
sum(case when lower(drug) in ('peptide','paracetamol')
then 1 else 0 end) as aspirin
from db.Team01.Table_1
group by subject_id
using like keyword
select subject_id,
sum(case when lower(drug) like '%cortisol%'
or lower(drug) like '%cortisone%'
or lower(drug) like '%dexamethasone%'
then 1 else 0 end) as steroids,
sum(case when lower(drug) like '%peptide%'
or lower(drug) like '%paracetamol%'
then 1 else 0 end) as aspirin
from db.Team01.Table_1
group by subject_id
Another potentially more intutive solution would be to use the BigQuery Contains_Substr
to return boolean results.
I've not used BigQuery but have been reading the docs researching it. I came across this while looking into impact of choosing collation at design stage.
I'm either wrong or this is a new feature since answers above.
CONTAINS_SUBSTR
https://cloud.google.com/bigquery/docs/reference/standard-sql/string_functions#contains_substr
Performs a normalized, case-insensitive search to see if a value
exists as a substring in an expression. Returns TRUE if the value
exists, otherwise returns FALSE.
Before values are compared, they are normalized and case folded with
NFKC normalization. Wildcard searches are not supported.

Counting records that contain letters given in (SQL)

I have to count records containing given letters, for example column A will contain count of records containing 'a' or 'A', and for E it will be count of records containing 'e' or 'E'. Is there any way to do this by only using grouping functions?
I can do this by using subqueries, but we had this task in class before learning subqueries and I have no idea how to do this by grouping.
The result of the code below that I want to achieve by using grouping:
select
(select count(*) from table where lower(name) like '%a%') as a,
(select count(*) from table where lower(name) like '%e%') as e
from dual;
you can use count + case to avoid repeating full-table query select
select count(case when lower(name) like '%a%' then 1 end) as a
,count(case when lower(name) like '%e%' then 1 end) as e
from Table
The proper expression uses sum():
select sum(case when lower(name) like '%a%' then 1 else 0 end) as num_a,
sum(case when lower(name) like '%e%' then 1 else 0 end) as num_e
from t;
You can also use regular expressions (although they are probably more expensive than like for this purpose):
select sum(case when regexp_like(name, '[aA]') then 1 else 0 end) as num_a,
sum(case when regexp_like(name, '[eE]') then 1 else 0 end) as num_e
from t;

Problems with my WHERE clause (SQL)

I'm trying to write a query that returns the following columns:
owner_id,
number_of_concluded_bookings,
number_of_declined_bookings,
number_of_inquiries
However, the problem is that my WHERE clause messes up the query because I am querying the same table. Here is the code:
SELECT owner_id,
Count(*) AS number_of_cancelled_bookings
FROM bookings
WHERE state IN ('cancelled')
GROUP BY owner_id
ORDER BY 1;
It's easy to retrieve the columns individually, but I want all of them. Say I wanted number_of_concluded_bookings as well, that would mean I'd have to alter the WHERE clause ...
Help is greatly appreciated!
Consider conditional aggregations:
SELECT owner_id,
SUM(CASE WHEN state='concluded' THEN 1 ELSE 0 END) AS number_of_concluded_bookings,
SUM(CASE WHEN state='cancelled' THEN 1 ELSE 0 END) AS number_of_cancelled_bookings,
SUM(CASE WHEN state='declined' THEN 1 ELSE 0 END) AS number_of_declined_bookings,
SUM(CASE WHEN state='inquiries' THEN 1 ELSE 0 END) AS number_of_inquiries
FROM bookings
GROUP BY owner_id