Complex case statement using group - sql

I have the following table:
tbl
ID vaccine_code vaccine_day
A 1A 0
A 2A 30
B moderna,1A 0
B moderna,2A 35
C moderna 0
C moderna 25
there are various ways in which the vaccines are coded in my database. 1A denotes first dose and 2A denotes second dose. At times, as in ID='C', the dose number is not denoted. In that case, I need to use the value in the vaccine_day column to extrapolate dose information.
I am looking for the following:
ID vaccine_dose vaccine_day
A dose1 0
A dose2 30
B dose1 5
B dose2 35
C dose1 0
C dose2 25
So far, I have:
select ID,
case when vaccine_code like '%1A%' then 'dose1'
case when vaccine_code like '%2A%' then 'dose2'
case when vaccine_code = 'moderna' and min(vaccine_day) then 'dose1'
case when vaccine_code = 'moderna' and max(vaccine_day) then 'dose1'
from tbl
group by vaccine_day;

You have a few issues with your query.
You don't end your case.
You group by vaccine_day which will return you each vaccine_day.
Didn't test it as I don't have the full sample data, both your sample data are the same id and days, but you can start with something like this:
select id, case when vaccine_code like '%1A%' then 'dose1'
when vaccine_code like '%2A%' then 'dose2'
when vaccine_code = 'moderna' and vaccine_day = min(vaccine_day) then 'dose1'
when vaccine_code = 'moderna' and vaccine_day = max(vaccine_day) then 'dose1'
end as vaccine_dose
from tbl
group by id, vaccine_code;

Related

A query to select bigger value out of two which are stored in the same table, but in different rows

I have got a following table named "Details":
Shop_number
Reference_number
Value
222222
48
263.900
222222
54
721.400
222223
48
125.100
222223
54
82.900
222224
48
329.800
222224
54
218.200
I would like to find/select all store number records where value of reference_number 48 is bigger than value of reference_number 54, so I would like 222223 and 222224 to be returned from my query.
I feel like it should be done with HAVING clause, but I'm not quite sure how to use it in this case. Problably also joining the same table may be advised to use in select query.
Yes you can use HAVING for this
SELECT Shop_number
FROM Details
GROUP BY Shop_number
HAVING MAX(CASE WHEN Reference_number = 48 THEN Value END) >
MAX(CASE WHEN Reference_number = 54 THEN Value END)
Another approach, as you mentioned with self-join:
SELECT a.Shop_number, a.Value AS Value48, b.Value as Value54
FROM details a
INNER JOIN details b
ON a.Shop_number = b.Shop_number
AND a.Reference_number = 48
AND b.Reference_number = 54
WHERE a.Value > b.Value
Use a pivot aggregation approach:
SELECT Shop_number
FROM Details
GROUP BY Shop_number
HAVING MAX(CASE WHEN Reference_number = 48 THEN Value END) >
MAX(CASE WHEN Reference_number = 54 THEN Value END);
Or you can just check that other value via subquery:
SELECT DISTINCT Shop_number
FROM Details d1
WHERE Reference_number=48
AND Value > (
select top 1 Value
from Details
where Shop_number=d1.Shop_number
and Reference_number=54
)

Aggregate function of column that has at least one type of value in a different column

I have table below as
ID | Blood_Type | Size
B2 A 100
B2 B 200
C2 C 102
C2 O 88
G4 I 44
G4 A 100
How can I query my table above to get average of only my IDs that have at least have one row with blood type of A
Expected output:
ID | Avg_Size
B2 150
G4 72
Thanks!
Tim's answer is good, a simpler albeit perhaps not how you would want to do it, other way, is doing HAVING in long form
SELECT id,
avg_size
FROM (
SELECT id,
AVG(size) AS avg_size,
SUM(IFF(blood_type = 'A', 1, 0)) AS a_count
FROM table
GROUP BY id
)
WHERE a_count > 1;
so you can ether use SUM or COUNT, they both ignore nulls, which is the implicit result of Tim's CASE WHEN Blood_Type = 'A' THEN 1 END is the same as
CASE
WHEN Blood_Type = 'A' THEN 1
ELSE NULL
END
if you use SUM it can handle null's or zeros thus the IFF can be used, which I like as it's smaller and more explicit about what is happening.
thus Tim's answer can be swapped to a SUM(IFF
like:
SELECT
id,
AVG(size) AS avg_size
FROM table
GROUP BY id
HAVING SUM(IFF(Blood_Type = 'A',1, 0) > 0;
You may try aggregation here with an assertion in the HAVING clause:
SELECT ID, AVG(Size) AS Avg_Size
FROM yourTable
GROUP BY ID
HAVING COUNT(CASE WHEN Blood_Type = 'A' THEN 1 END) > 0;

how can I add a new column where there may be matches on an id field

I currently have a table that contains an id, and a count of a criteria for that id field. For example my table looks like this:
ID Banana_count
1 13
2 23
3 56
The original counts came from a join and a query from other tables.
create FRUIT_TABLE as
select id, count (fruit)
from my_table a
where exists (select null from DATE_FED b
where a.id = b.id
and date = (2/11/17)
and fruit_type = 'banana')
group by id;
My question is, how can i add other attributes to this particular table so that it looks like:
ID Banana_count Apple_count Orange_count
1 13 35 22
2 23 44
3 56
4 33 55
5 11
I will have to add more ids to FRUIT_TABLE that may not already be in the current table, but for fruits that are currently associated with an id, i'd like to add them in the same row.
This is a classic use case for merge:
merge into fruit_table
using apple_table
on (fruit_table.id = apple_table.id)
when matched then update set
fruit_table.apples = apple_table.apples
when not matched then insert (id,apples)
values(
apple_table.id,
apple_table.apples
);
I have simplified the problem slightly so that you are inserting from a table that simply has ids and a count of apples, so that the structure of the merge is clearer. But you can insert a subquery instead into the using... section of the statement to meet your actual requirements.
I would look into something like the following [you didn't provide your table definitions, or other application or requirements constraints so an exact answer is not possible]:
create FRUIT_TABLE as
select id
, sum(case when fruit_type = 'banana' then 1 else 0 end ) Banana_count
, sum(case when fruit_type = 'apple' then 1 else 0 end ) apple_count
, sum(case when fruit_type = 'orange' then 1 else 0 end ) orange_count
from my_table a
group by id;

How to select all the records even if there is no data in sql

I have a query like
SELECT 'Education' as Purpose,ate_sex, Age_range, COUNT(*) AS Total
FROM (
SELECT dbo.ClientMain.ate_sex, dbo.ClientMain.ate_Id,
CASE
WHEN ate_Age BETWEEN 15 AND 19 THEN '15-19'
WHEN ate_Age BETWEEN 20 AND 24 THEN '20-24'
WHEN ate_Age BETWEEN 25 AND 34 THEN '25-34'
WHEN ate_Age BETWEEN 35 AND 44 THEN '35-44'
WHEN ate_Age BETWEEN 45 AND 54 THEN '45-54'
WHEN ate_Age BETWEEN 55 AND 64 THEN '55-64'
ELSE '80+' END AS Age_range
FROM dbo.ClientMain
INNER JOIN dbo.ClientSub ON dbo.ClientMain.ate_Id = dbo.ClientSub.ate_Id
WHERE (dbo.ClientSub.chk_Name = N'Assistance')
) AS t group by ate_sex,Age_range
Which returns the data as:
But I want my result as when there is no record with he age range in 15-19, it have to return zero. As Education Male 15-19 0
These are my tables
Can anybody please modify my query to get zeros for no records.
Use a LEFT JOIN instead of INNER JOIN
Use ISNULL(COUNT(*), 0) instead of COUNT(*). Alternatively, you can also use
Total = CASE WHEN COUNT(*) IS NULL THEN 0 ELSE COUNT(*) END
Try This,
case when COUNT(*) = 1 then 0 else COUNT(*) end as exp
If you doesnt have data with male, 15-19 in any table, yet you still want it in result with count as "0". Try building a static table with possible rows and do a cross join with your resultset.

SQL Server 2008 - query to order by a column with odd and even numbers

I'm in trouble and I need a quick answer, please.
I have a table in which I have a column containing only odd and even numbers.
I want to select first only the rows with the orderByColumn containing odd numbers, then add the result to the "even query"
My table looks like this
col1 col2 orderByColumn col4
...................................
c c 44 c
c c 45 c
c c 46 c
...................................
...................................
I guess I should make an union, which I think would look like this:
select * from myTable
where [orderByColumn] % 2 = 0 --order by [orderByColumn]-->error
UNION
select * from myTable
where [orderByColumn] % 2 > 0 order by [orderByColumn]
The query does union the 2 selects, but obviously, they are merged and order by orderByColumn which I don't want to.
I want the results to look like this:
c c 44 c
c c 46 c
c c 48 c
........
c c 45 c
c c 47 c
c c 49 c
........
Any ideas ? Thank you:)
Try this for the even numbers first:
select *
from myTable
order by [orderByColumn]%2, orderbycolumn
Use desc if you want the odd numbers first.
You could also introduce a column with your sort value, like
select *,
case when [orderByColumn]%2=0 then 0 else 1 end as isOdd
from myTable
order by
isodd , orderbyColumn
try this
select
OrderByColumn
from
table
where
mod(OrderByColumn,2) = 0
union all
select
OrderByColumn
from
table
where
mod(OrderByColumn,2) >0