Performing Count() and Avg() within a Case statement - tsql

I am using this CASE statement to show a total count of visits and average length of visit grouped by age groups:
CASE WHEN AgeCalcSort = 0 AND AgeCalcSort <= 1 then (
Count(VisitID),
AVG(DATEDIFF(dd,StartDate,EndDate ))
)
WHEN AgeCalcSort >= 2 AND AgeCalcSort <= 17 then (
Count(VisitID),
AVG(DATEDIFF(dd,StartDate,EndDate ))
)
WHEN AgeCalcSort >= 18 AND AgeCalcSort <= 64 then (
Count(VisitID),
AVG(DATEDIFF(dd,StartDate,EndDate ))
)
WHEN AgeCalcSort >= 65 then (
Count(VisitID),
AVG(DATEDIFF(dd,StartDate,EndDate ))
)
END,
The result should look like this:
Age 1 Count Age 1 Avg LOS Age 2 Count Age 1 Avg LOS Age 3 Count Age 3 Avg LOS Age 4 Count Age 4 Avg LOS
5 5.3 18 9.2 20 12 0 0
Can anyone adivse what am I doing wrong, or a better way to achieve the end result? Thanks In advance.

You have mentioned 2 points:
1) Can anyone adivse what am I doing wrong
You are using the CASE expression in your query incorrectly.
You cannot have 2 columns returned when some condition is satisfied in CASE statement.
As per Microsoft documentation, CASE evaluates a list of conditions and returns one of multiple possible result expressions.
Check this Microsoft white paper for further study Ref. URL
2) a better way to achieve the end result
There can be many ways to achieve this.
Below is one of the simple way. try the query below:
SELECT
SUM(CASE WHEN AgeCalcSort = 0 AND AgeCalcSort <= 1 THEN 1 ELSE 0 END) [Age 1 Count],
AVG(CASE WHEN AgeCalcSort = 0 AND AgeCalcSort <= 1 THEN DATEDIFF(DAY,StartDate,EndDate) ELSE NULL END) [Age 1 Avg LOS]
SUM(CASE WHEN AgeCalcSort = 2 AND AgeCalcSort <= 17 THEN 1 ELSE 0 END) [Age 2 Count],
AVG(CASE WHEN AgeCalcSort = 2 AND AgeCalcSort <= 17 THEN DATEDIFF(DAY,StartDate,EndDate) ELSE NULL END) [Age 2 Avg LOS]
SUM(CASE WHEN AgeCalcSort = 18 AND AgeCalcSort <= 64 THEN 1 ELSE 0 END) [Age 3 Count],
AVG(CASE WHEN AgeCalcSort = 18 AND AgeCalcSort <= 64 THEN DATEDIFF(DAY,StartDate,EndDate) ELSE NULL END) [Age 3 Avg LOS]
SUM(CASE WHEN AgeCalcSort >= 65 THEN 1 ELSE 0 END) [Age 4 Count],
AVG(CASE WHEN AgeCalcSort >= 65 THEN DATEDIFF(DAY,StartDate,EndDate) ELSE NULL END) [Age 4 Avg LOS]
FROM [YourTableName]
Query explanation
I have achieved count by using SUM, if you look into any of count logic you will notice it says when it satisfies condition 1 else 0. and that is summed later using SUM. So that all 1's ultimately gives count.
for average you will notice that then condition is not satisfied I am using NULL, which is intentional, to avoid wrong math.
for in TSQL query AVG of (2,4,NULL) is 3, while AVG of (2,4,0) is 2. so in your case NULL will help to avoid messing average value.

A case expression returns a single value, which you may then count or average.
It is not possible to produce multiple columns of output from a singe case expression. So, you need one case expression for each wanted column of output, like this:
select
COUNT(CASE WHEN AgeCalcSort = 0 AND AgeCalcSort <= 1 then VisitID end)
, AVG(CASE WHEN AgeCalcSort = 0 AND AgeCalcSort <= 1 then DATEDIFF(dd,StartDate,EndDate ) end)
, COUNT(CASE WHEN AgeCalcSort = 2 AND AgeCalcSort <= 17 then VisitID end)
, AVG(CASE WHEN AgeCalcSort = 2 AND AgeCalcSort <= 17 then DATEDIFF(dd,StartDate,EndDate ) end)
, COUNT(CASE WHEN AgeCalcSort = 18 AND AgeCalcSort <= 64 then VisitID end)
, AVG(CASE WHEN AgeCalcSort = 18 AND AgeCalcSort <= 64 then DATEDIFF(dd,StartDate,EndDate ) end)
, COUNT(CASE WHEN AgeCalcSort = 65 then VisitID end)
, AVG(CASE WHEN AgeCalcSort = 65 then DATEDIFF(dd,StartDate,EndDate ) end)
from ...
Notes:
place each case expression inside the relevant aggregate function
the count() function ignores NULLs so if the when conditions are not met the count will not increment
whilst T-SQL documentation can sometimes use the term "case statement" technically that is incorrect; e.g. a complete select query is a "statement", and "expressions" evaluate to single values, hence the correct term is case expression

Related

order by with operation on select case query

I want to rank a table by multi-columns and sum of each point that is decided by value.
For example, I can get some columns and each points by below query.
select
(case when seller=true then 50 else 0 end) as sel,
(case when buyer=true then 40 else 0 end) as buy
from company;
but I can't order by this values by like this query
select
(case when seller=true then 50 else 0 end) as sel,
(case when buyer=true then 40 else 0 end) as buy
from company
order by (sel + by);
or this
select
(case when seller=true then 50 else 0 end) as sel,
(case when buyer=true then 40 else 0 end) as buy,
(sell, buy) as sm
from company
order by sm;
How can I do that?
Oh, sorry, I found the answer.
select * from
(select
(case when seller=true then 50 else 0 end) as sel,
(case when buyer=true then 40 else 0 end) as buy
from company) as tmp
order by (tmp.sel + tmp.buy);

Postgresql: Change COUNT value based on condition

So this is my table in database:
Worker X have this work result BETWEEN '2015-06-01' AND '2015-06-06':
What I want to do is to count the number of work days but my condition is that if (nb_heures + nb_heures_s) > 4 I count it 1 day but if (nb_heures + nb_heures_s) <= 4 I count it 0.5 day.
So the result I must get from this table 5.5 work days and not 6.
I tried this query but it's not working well:
SELECT
count(CASE WHEN (nb_heures + nb_heures_s) > 4 THEN 1 END) as full_day_work,
count(CASE WHEN (nb_heures + nb_heures_s) <= 4 THEN 0.5 END) as half_day_work
FROM pointage_full pf
WHERE date_pointage BETWEEN '2015-06-01' AND '2015-06-06'
AND pf.id_salarie = 5
How can I reach my objectif ?
COUNT(expr) always returns a bigint, as it simply returns the number of rows for which expr is not NULL.
You can use SUM instead :
SELECT SUM(CASE WHEN (nb_heures + nb_heures_s) > 4 THEN 1 ELSE 0.5 END) as number_of_days
FROM pointage_full pf
WHERE date_pointage BETWEEN '2015-06-01' AND '2015-06-06';

Union Statement that can be changed to Case statement

Good Day Everyone!
well i have this kind of code and it kinda ugly,
a friend of mine told me i can implement Case Statements in here, but i do not know how or how would i implement, the code is long so if you could just help me to optimize my code i would appreciate it greatly!
PS. please be gentle to me, im new in T-sql :)
Thank yoU!
SELECT
SUM(CYJEWELRY) 'CY_Jewelry'
,SUM(CYAPPLICANCE) 'CY_Appliance'
,SUM(CYCELLPHONE) 'CY_Cellphone'
,SUM(PYJEWELRY) 'PY_Jewelry'
,SUM(PYAPPLIANCE) 'PY_Appliance'
,SUM(PYCELLPHONE) 'PY_Cellphone'
FROM
(
---TOTAL NUNG A FORMAT 0,0,0,0,0,0
--------------CURRENT YEAR JEWELRY
SELECT COUNT (*) AS CYJEWELRY,0 AS CYAPPLICANCE,0 AS CYCELLPHONE,0 AS PYJEWELRY,0 AS PYAPPLIANCE,0 AS PYCELLPHONE
FROM #TEMPTABLE1
WHERE (fld_StorageGroupID >= 3 and fld_StorageGroupID <= 14)
UNION
-----------CURRENT YEAR APPLIANCE
SELECT 0,COUNT(*),0,0,0,0
FROM #TEMPTABLE1
WHERE fld_StorageGroupID = 1
UNION
------------CURRENT YEAR CELLPHONE
SELECT 0,0,COUNT(*),0,0,0
FROM #TEMPTABLE1
WHERE fld_StorageGroupID = 2
UNION
---------------LAST YEAR JEWELRY
SELECT 0,0,0,COUNT(*),0,0
FROM #TEMPTABLE2
WHERE (fld_StorageGroupID >= 3 and fld_StorageGroupID <= 14)
UNION
-----------------------LAST YEAR APPLIANCE
SELECT 0,0,0,0,COUNT (*),0
FROM #TEMPTABLE2
WHERE fld_StorageGroupID = 1
UNION
-------------------------LAST YEAR CELLPHONE
SELECT 0,0,0,0,0,COUNT(*)
FROM #TEMPTABLE2
WHERE fld_StorageGroupID = 2
)A
Assuming your data is bit like this Sql Fiddle Example, try this for the sub query using SUM() and CASE.
SELECT SUM(CASE WHEN fld_StorageGroupID >= 3 and fld_StorageGroupID <= 14 ELSE 0 END) Col1And4,
SUM(CASE WHEN fld_StorageGroupID = 1 THEN 1 ELSE 0 END) Col2And5,
SUM(CASE WHEN fld_StorageGroupID = 2 THEN 1 ELSE 0 END) Col3And6
FROM #TEMPTABLE1
GROUP BY fld_StorageGroupID
Since you are applying the same filter for last 3 columns in the subquery, I have done only first 3 columns here.
EDIT:
I think this is better than above (Note: no need to use SUM() in the main query).
Fiddle Example with data
select col1_4 CY_Jewelry,
col2_5 CY_Appliance,
col3_6 CY_Cellphone,
col1_4 PY_Jewelry,
col2_5 PY_Appliance,
col3_6 PY_Cellphone
from (
select sum(case when id>= 3 and id <= 14 then 1 else 0 end) col1_4,
sum(case when id = 2 then 1 else 0 end) col2_5,
sum(case when id = 3 then 1 else 0 end) col3_6
from t
--group by id
) X

Sum up items between setup of custom times

We need to count the number of items that occur 10 minutes before and 10 minutes after the hour, by day. We have a table that tracks the items individually. Ideally i would like to have the output be something like the below, but and totally open to other suggestions.
Table - Attendance
Att_item timestamp
1 2012-09-12 18:08:00
2 2012-09-01 23:26:00
3 2012-09-23 09:33:00
4 2012-09-11 09:43:00
5 2012-09-06 05:57:00
6 2012-09-17 19:26:00
7 2012-09-06 10:51:00
8 2012-09-19 09:42:00
9 2012-09-06 13:55:00
10 2012-09-05 07:26:00
11 2012-09-02 03:08:00
12 2012-09-19 12:17:00
13 2012-09-12 18:14:00
14 2012-09-12 18:14:00
Output
Date Timeslot_5pm Timeslot_6pm Timeslot_7pm
9/11/2012 11 22 22
9/12/2012 30 21 55
9/13/2012 44 33 44
Your requirements are not totally clear, but if you only want to count the number of records in the 20 minute window:
select cast(tstmp as date) date,
sum(case when datepart(hour, tstmp) = 1 then 1 else 0 end) Timeslot_1am,
sum(case when datepart(hour, tstmp) = 2 then 1 else 0 end) Timeslot_2am,
sum(case when datepart(hour, tstmp) = 3 then 1 else 0 end) Timeslot_3am,
sum(case when datepart(hour, tstmp) = 4 then 1 else 0 end) Timeslot_4am,
sum(case when datepart(hour, tstmp) = 5 then 1 else 0 end) Timeslot_5am,
sum(case when datepart(hour, tstmp) = 6 then 1 else 0 end) Timeslot_6am,
sum(case when datepart(hour, tstmp) = 7 then 1 else 0 end) Timeslot_7am,
sum(case when datepart(hour, tstmp) = 8 then 1 else 0 end) Timeslot_8am,
sum(case when datepart(hour, tstmp) = 9 then 1 else 0 end) Timeslot_9am,
sum(case when datepart(hour, tstmp) = 10 then 1 else 0 end) Timeslot_10am,
sum(case when datepart(hour, tstmp) = 11 then 1 else 0 end) Timeslot_11am,
sum(case when datepart(hour, tstmp) = 12 then 1 else 0 end) Timeslot_12pm,
sum(case when datepart(hour, tstmp) = 13 then 1 else 0 end) Timeslot_1pm,
sum(case when datepart(hour, tstmp) = 14 then 1 else 0 end) Timeslot_2pm,
sum(case when datepart(hour, tstmp) = 15 then 1 else 0 end) Timeslot_3pm,
sum(case when datepart(hour, tstmp) = 16 then 1 else 0 end) Timeslot_4pm,
sum(case when datepart(hour, tstmp) = 17 then 1 else 0 end) Timeslot_5pm,
sum(case when datepart(hour, tstmp) = 18 then 1 else 0 end) Timeslot_6pm,
sum(case when datepart(hour, tstmp) = 19 then 1 else 0 end) Timeslot_7pm,
sum(case when datepart(hour, tstmp) = 20 then 1 else 0 end) Timeslot_8pm,
sum(case when datepart(hour, tstmp) = 21 then 1 else 0 end) Timeslot_9pm,
sum(case when datepart(hour, tstmp) = 22 then 1 else 0 end) Timeslot_10pm,
sum(case when datepart(hour, tstmp) = 23 then 1 else 0 end) Timeslot_11pm
from yourtable
where datepart(minute, tstmp) >= 50
or datepart(minute, tstmp) <= 10
group by cast(tstmp as date)
If you want to count the number of records within each hour plus the records that are in the >=50 and <= 10 timeframe, then you will have to adjust this.
This does just one column (well 4 but you get my point).
select DATEPART(YYYY, FTSdate) as [year], DATEPART(mm, FTSdate) as [month]
, DATEPART(dd, FTSdate) as [day], DATEPART(hh, FTSdate) as [hour], COUNT(*)
from [Gabe2a].[dbo].[docSVsys]
where DATEPART(mi, FTSdate) >= 50 or DATEPART(mi, FTSdate) <= 10
group by DATEPART(YYYY, FTSdate), DATEPART(mm, FTSdate), DATEPART(dd, FTSdate), DATEPART(hh, FTSdate)
order by DATEPART(YYYY, FTSdate), DATEPART(mm, FTSdate), DATEPART(dd, FTSdate), DATEPART(hh, FTSdate)
Separate columns.
select DATEPART(YYYY, FTSdate) as [year], DATEPART(mm, FTSdate) as [month]
, DATEPART(dd, FTSdate) as [day]
, sum(case when DATEPART(hh, FTSdate) = '0' then 1 else 0 end) as [0:00] -- midnight
, sum(case when DATEPART(hh, FTSdate) = '1' then 1 else 0 end) as [1:00]
, sum(case when DATEPART(hh, FTSdate) = '2' then 1 else 0 end) as [2:00]
, sum(case when DATEPART(hh, FTSdate) = '3' then 1 else 0 end) as [3:00]
, sum(case when DATEPART(hh, FTSdate) = '4' then 1 else 0 end) as [4:00]
from [Gabe2a].[dbo].[docSVsys]
where DATEPART(mi, FTSdate) >= 50 or DATEPART(mi, FTSdate) <= 10
group by DATEPART(YYYY, FTSdate), DATEPART(mm, FTSdate), DATEPART(dd, FTSdate)
order by DATEPART(YYYY, FTSdate), DATEPART(mm, FTSdate), DATEPART(dd, FTSdate)

Count valid values per user

I have a table with a list of values. -1 is a blank value:
ID FieldType1A FieldType1B FieldType2A FieldType2B Person
1 15 14 10 -1 1
2 16 -1 12 10 1
3 17 -1 5 6 1
4 6 -1 7 -1 2
...
So the result should be:
Person FieldType1 FieldType2
1 4 5
2 1 1
there is a users table with a list of user IDs, would there be a way of iterating over that list of values to generate the person list in the result set (0 for the field types being perfectly valid as it is merely counts)? I think the answer to T-SQL Column Values Count is a step in the direction I'm attempting to go, but unsure how to combine columns that are the same (the A/Bs allow for a list of answers). That and I'm interested in combining all valid values as not attempting to count the number of each valid response.
You can use a CASE expression to change all non-negative-one values to 1, and -1 values to 0, and then sum them up.
SELECT Person,
SUM(CASE WHEN FieldType1A <> -1 THEN 1 ELSE 0 END) +
SUM(CASE WHEN FieldType1B <> -1 THEN 1 ELSE 0 END) AS FieldType1,
SUM(CASE WHEN FieldType2A <> -1 THEN 1 ELSE 0 END) +
SUM(CASE WHEN FieldType2B <> -1 THEN 1 ELSE 0 END) AS FieldType2
FROM YourTable
GROUP BY Person
SELECT Person,
count(nullif(FieldType1A, -1)) + count(nullif(FieldType1B, -1)) as FieldType1,
count(nullif(FieldType2A, -1)) + count(nullif(FieldType2B, -1)) as FieldType2
FROM yourtable
GROUP BY person