calculate SUM of multiple Case When - sql

My data has 3 groups of variables (set1_a,b,c,) (set2_x,y), (set3_n) as below. For each group, if at least 1 variable has value>90 then I count as 1.
Then, I SUM the count.
My code below works fine. However, I would like to put all in 1 select statement.
Can you please help?
Create TABLE have (
id varchar(225),
set1_a varchar(225),
set1_b varchar(225),
set1_c varchar(225),
set2_x varchar(225),
set2_y varchar(225),
set3_n varchar(225)
);
Insert into have (id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n) values (1,1,3,200,1,1,5);
Insert into have (id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n) values (2,1,3,200,200,1,5);
Insert into have (id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n) values (3,1,3,200,200,1,500);
Insert into have (id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n) values (4,1,3,1,1,1,500);
select * from have;
SELECT id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n,N1,N2, count1+count2+count3 as total_count FROM
(
select id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n,
case
when (set1_a >90 or set1_b>90 or set1_c>90) then 1 else 0
end as count1,
case
when (set2_x >90 or set2_y>90) then 1 else 0
end as count2
case
when (set3_n >90) then 1 else 0
end as count3
from have
)
--WHERE N1+N2>=2
;

This works. Also, ideally the data type should be int and not varchar since you are comparing with int.
SELECT id,set1_a,set1_b,set1_c,set2_x,set2_y,set3_n,
(case
when greatest(
cast(set1_a as int),
cast(set1_b as int),
cast(set1_c as int)
)>90 then 1 else 0 end) +
(case
when greatest(
cast(set2_x as int),
cast(set2_y as int)
)>90 then 1 else 0 end) +
(case
when cast(set3_n as int) > 90
then 1 else 0 end
) as total_count
from have
DB fiddle on postgres 11 for the same. But it's all standard sql.

Related

How to count records in a table based on each (per) hour in SQL Server?

In SQL Server 2008, I have data like this (Case: varchar(20), Time: time):
Case Time
-------------
D1 18:44
D2 19:12
C1 21:20
F2 21:05
...
What I would like to do is to count cases per hour. Should include all cases.
Expected result:
.... Column18 Column19 Column20 Column21 ...
1 1 0 2
where Column18 refers to the cases between 18:00 and 18:59, and same logic for others. I have from Column0 to Column23, 1 column per hour...
What I am doing is:
Select
...
, Column18 = sum(CASE WHEN Time like '18:%' THEN 1 ELSE 0 END)
, Column19 = sum(CASE WHEN Time like '19:%' THEN 1 ELSE 0 END)
, Column20 = sum(CASE WHEN Time like '20:%' THEN 1 ELSE 0 END)
, Column21 = sum(CASE WHEN Time like '21:%' THEN 1 ELSE 0 END)
...
from
mytable
Even though my query works, it is long and repetitive, so it does not seem professional to me. I wonder if there is any better way to handle this situation. Any advice would be appreciated.
We can go with Dynamic Pivot -
declare #ColString varchar(1000)=''
;with cte as(
select 0 as X
union all
select x+1 as X
from cte where X <23
)
select #ColString = #ColString + ',[Column' + cast(X as varchar) + ']' from cte
select #ColString = stuff(#ColString,1,1,'')
declare #DynamicQuery varchar(3000)=''
select #DynamicQuery =
'select *
from (
select [case],''Column''+cast(datepart(hh,[time]) as varchar) as [time]
from #xyz
) src
pivot
(
count([case]) for [Time] in ('+ #ColString + ')
) piv'
exec (#DynamicQuery)
Input data -
create table #xyz ([Case] varchar(10),[Time] time(0))
insert into #xyz
select 'D1','18:44' union all
select 'D2','19:12' union all
select 'C1','21:20' union all
select 'F2','21:05'
Your query is basically fine, but I strongly discourage you from using string functions on date/time columns.
datepart() is definitely one solution:
Select ...,
Column18 = sum(CASE WHEN datepart(hour, Time) = 18 THEN 1 ELSE 0 END)
Column19 = sum(CASE WHEN datepart(hour, Time) = 19 THEN 1 ELSE 0 END)
Direct comparison is more verbose, but more flexible:
select . . .,
sum(case when time >= '18:00' and time < '19:00' then 1 else 0 end) as column18,
sum(case when time >= '19:00' and time < '20:00' then 1 else 0 end) as column19,
Note that this uses as. SQL Server supports the syntax alias =. However, other databases do not use such syntax, so I prefer to stick with the ANSI-standard method of defining aliases.
Putting the values on rows instead of columns is probably the more "typical" solution:
select datepart(time, hour) as hr, count(*)
from t
group by datepart(time, hour)
order by hr;
As written, this will not return hours with zero counts.
Here is the simplest answer I could come up. Thanks a lot for all the advices. Looks way better now:
create table #temp (CaseID varchar(20),TheTime time)
insert into #temp values ('A1','03:56')
insert into #temp values ('A2','03:12')
insert into #temp values ('B2','03:21')
insert into #temp values ('C1','05:12')
insert into #temp values ('B3','06:00')
insert into #temp values ('B4','07:14')
insert into #temp values ('B5','07:18')
insert into #temp values ('D1','18:44')
insert into #temp values ('D2','19:54')
insert into #temp values ('C2','21:12')
insert into #temp values ('F4','21:50')
select *
from (
select CaseID, DATEPART(hour,TheTime) as HourOfDay
from #temp
) t
PIVOT
(
Count(CaseID)
FOR HourOfDay IN ([00],[01],[02],[03],[04],[05],[06],[07],[08],
[09],[10],[11],[12],[13],[14],[15],[16],[17],
[18],[19],[20],[21],[22],[23])
) AS PivotTable

getting average of average depending on a column?

I have a query where I get the average in each row and showing the employee.
I would like it to show the average for EACH employee. meaning I would like to average all the row with the same employee.
How would I be able to accomplish this?
This is my current query:
SELECT
(
SELECT AVG(rating)
FROM (VALUES
(cast(c.rating1 as Float)),
(cast(c.rating2 as Float)),
(cast(c.rating3 as Float)),
(cast(c.rating4 as Float)),
(cast(c.rating5 as Float))
) AS v(rating)
WHERE v.rating > 0
) avg_rating, employee
From CSEReduxResponses c
Where
month(c.approveddate)= 6
AND year(c.approveddate)=2014
Below I have some sample data I created:
create table CSEReduxResponses (rating1 int, rating2 int, rating3 int, rating4 int, rating5 int,
approveddate datetime,employee int)
insert into CSEReduxResponses (rating1 , rating2 ,rating3 , rating4 , rating5 ,
approveddate, employee )
values
(5,4,5,1,4,'2014-06-18',1),
(5,4,5,1,4,'2014-06-18',1),
(5,4,5,1,0,'2014-06-18',1),
(5,4,0,1,4,'2014-06-18',2),
(5,4,5,1,4,'2014-06-18',2),
(5,4,0,1,4,'2014-06-18',3),
(5,0,5,4,4,'2014-06-18',3),
(5,4,5,0,0,'2014-06-18',3);
How about something like this?
select employee,
avg(case when n.n = 1 and rating1 > 0 then rating1
when n.n = 2 and rating2 > 0 then rating2
when n.n = 3 and rating3 > 0 then rating3
when n.n = 4 and rating4 > 0 then rating4
when n.n = 5 and rating5 > 0 then rating5
end)
from CSEReduxResponses c cross join
(select 1 as n union all select 2 union all select 3 union all select 4 union all select 5
) n
where month(c.approveddate)= 6 and year(c.approveddate)=2014
group by employee;
I would recommend rewriting the where clause as:
where c.approveddate >= '2014-06-01' and c.approveddate < '2014-07-01'
This would allow the SQL engine to use an index on approveddate.
select
(sum(rating1)+sum(rating2)+sum(rating3)+sum(rating4)+sum(rating5))
/
(count(nullif(rating1,0))+count(nullif(rating2,0))+count(nullif(rating3,0))+count(nullif(rating4,0))+count(nullif(rating5,0)))
as avg_rating,
count(*) as number_of_responses, employee
From CSEReduxResponses where month(approveddate)= 6 AND year(approveddate)=2014 group by employee ;
I have also come come up with a slightly slicker version, using a UDF. I prefer this one, as the average function might come in useful for other queries...
DELIMITER //
DROP FUNCTION IF EXISTS cca_wip.avg_ignore0//
CREATE FUNCTION cca_wip.avg_ignore0(
str VARCHAR(500)
) RETURNS double
COMMENT 'values separated by a coma, that are to be averaged. 0 will be treated as NULL'
BEGIN
DECLARE ss TEXT;
DECLARE sum, count double;
IF length(str)=0 or str not regexp '[0-9]' then RETURN 0;
end if;
IF str regexp '[a-z]' then RETURN NULL;
end if;
SET str=replace(str,'NULL','0');
SET sum =0;
SET count =0;
WHILE length(str)>0 DO
set ss=substring_index(str,',',1);
SET sum = sum + ss;
IF ss>0 THEN SET count = count+1;
END IF;
set str=trim(trim(trim(',') from trim(trim(ss from str))));
END WHILE;
RETURN (sum/count);
END//
DELIMITER ;
select
avg_ignore0(group_concat(concat_ws(',',rating1,rating2,rating3,rating4,rating5))),
count(*) as number_of_responses,
employee
From CSEReduxResponses
where
month(approveddate)= 6 AND year(approveddate)=2014
group by employee ;

How to get a non matching record in single row

Table1
ID
001
002
001
001
001
...
I want to check the id from table1 where id should be even. If id is different then i need to return 2 else 1
How to write a query for this?
For IDs
SELECT (CASE WHEN [ID]%2 = 1 THEN 1 ELSE 2 END)
FROM [table]
For ID COUNT :
SELECT (CASE WHEN COUNT([ID])%2 = 1 THEN 1 ELSE 2 END)
FROM [table]
GROUP BY [ID]
Please check this.
declare #t table (id varchar(50))
insert into #t values('001'),('001'),('002'),('002'),('001'),('002'),('002')
SELECT
CASE WHEN cast( [ID] as int) %2 = 1 THEN 1 ELSE 2 END oddOrEven
FROM #t
--for counting
;with cte as
(
SELECT [ID]%2 value,
CASE cast( [ID] as int) %2 when 1 THEN count(1) else count(2) END oddCount
FROM #t
group by id
)
select * from cte
If I understand the question correctly, a CASE statement is not necessary here. I'm assuming you want to return 2 when ID is even, and 1 when ID is odd? As long as there aren't any non-digit characters in the values of the ID column, you can do the following:
SELECT [ID], 2 - CAST([ID] AS int) % 2
FROM Table1
If you want to return 2 when ID is odd, and 1 when it is even (sorry, that wasn't clear from the question), then you can do this:
SELECT [ID], CAST([ID] AS int) % 2 + 1
FROM Table1

How to get only specified values record from multiple values of one Record in sql server

i have a table 'DEMO' like below, I need some specified result in my select query
PID Is_Viewed
1 False
1 False
1 False
1 False
2 True
2 False
Now i need to select only those record which if having even a single value 'True' then result will be 1 else 0 in select statment. i have tried much but not getting the desired result... my query is
/// select statement
SELECT distinct PId,CASE WHEN EXISTS(Select Is_Viewed from DEMO where Is_viewed=1) then 1 else 0 end as Is_viewed FROM DEMO
i WANT result like
PId Is_Viewed
1 0
2 1
Maybe something like this:
SELECT
PId,
MAX(CASE WHEN Is_Viewed='True' THEN 1 ELSE 0 END) AS Is_Viewed
FROM
DEMO
GROUP BY
PId
Edit
Considering the data that you have supplied.
DECLARE #tbl TABLE(PID INT,Is_Viewed VARCHAR(10))
INSERT INTO #tbl
VALUES
(1,'False'),
(1,'False'),
(1,'False'),
(1,'False'),
(2,'True'),
(2,'False')
With this query
SELECT
PId,
MAX(CASE WHEN Is_Viewed='True' THEN 1 ELSE 0 END) AS Is_Viewed
FROM
#tbl AS tbl
GROUP BY
PId
The output is this:
PId Is_Viewed
1 0
2 1
I guess that Is_Viewed is defined as a bit type since SQL server doesn't have a BOOLEAN type. Then try this:
SELECT PID,
MAX(CAST(Is_Viewed as INT)) as Is_Viewed
FROM T
GROUP BY PID
SQLFiddle demo
DECLARE #tbl TABLE(PID INT,Is_Viewed VARCHAR(10))
INSERT INTO #tbl
VALUES
(1,'False'),
(1,'False'),
(1,'False'),
(1,'False'),
(2,'True'),
(2,'False')
select distinct t.PID,
CASE WHEN t.Is_Viewed = 'TRUE'
THEN 1 ELSE 0 END As PType from #tbl t
INNER JOIN
(Select MIN(PID)As PID,Is_Viewed
FROM #tbl
GROUP BY Is_Viewed)AS KK
ON KK.PID = t.PID
AND KK.Is_Viewed = t.Is_Viewed

Getting average from 3 columns in SQL Server

I have a table with 3 columns (smallint) in SQL Server 2005.
Table Ratings
ratin1 smallint,
ratin2 smallint
ratin3 smallint
These columns can have values from 0 to 5.
How can I select the average value of these fields, but only compare fields where the value is greater then 0.
So if the column values are 1, 3 ,5 - the average has to be 3.
if the values are 0, 3, 5 - The average has to be 4.
This is kind of quick and dirty, but it will work...
SELECT (ratin1 + ratin2 + ratin3) /
((CASE WHEN ratin1 = 0 THEN 0 ELSE 1 END) +
(CASE WHEN ratin2 = 0 THEN 0 ELSE 1 END) +
(CASE WHEN ratin3 = 0 THEN 0 ELSE 1 END) +
(CASE WHEN ratin1 = 0 AND ratin2 = 0 AND ratin3 = 0 THEN 1 ELSE 0 END) AS Average
#mwigdahl - this breaks if any of the values are NULL. Use the NVL (value, default) to avoid this:
Sum columns with null values in oracle
Edit: This only works in Oracle. In TSQL, try encapsulating each field with an ISNULL() statement.
There should be an aggregate average function for sql server.
http://msdn.microsoft.com/en-us/library/ms177677.aspx
This is trickier than it looks, but you can do this:
SELECT dbo.MyAvg(ratin1, ratin2, ratin3) from TableRatings
If you create this function first:
CREATE FUNCTION [dbo].[MyAvg]
(
#a int,
#b int,
#c int
)
RETURNS int
AS
BEGIN
DECLARE #result int
DECLARE #divisor int
SELECT #divisor = 3
IF #a = 0 BEGIN SELECT #divisor = #divisor - 1 END
IF #b = 0 BEGIN SELECT #divisor = #divisor - 1 END
IF #c = 0 BEGIN SELECT #divisor = #divisor - 1 END
IF #divisor = 0
SELECT #result = 0
ELSE
SELECT #result = (#a + #b + #c) / #divisor
RETURN #Result
END
select
(
select avg(v)
from (values (Ratin1), (Ratin2), (Ratin3)) as value(v)
) as average
You can use the AVG() function. This will get the average for a column. So, you could nest a SELECT statement with the AVG() methods and then SELECT these values.
Pseudo:
SELECT col1, col2, col3
FROM (
SELECT AVG(col1) AS col1, AVG(col2) AS col2, AVG(col3) AS col3
FROM table
) as tbl
WHERE col1 IN (0, 3, 5)
etc.