sql get total of value across multiple columns - sql

I have a sql server 2012 db with a table with 10 columns
name test1, test2, test3, test4,....
bob yes no null yes
john no yes yes null
I want to get a total of all results from the 'test' fields so i want my results to be
yes = 4
no = 2
null = 2
I have tried using cte, sum, case when but I cant get the results i'm looking for. below is a sample of my sql if anyone could tell me how to go about getting the results i'm looking for.
with cte as
(
SELECT
test1,
sum (case when test1 = 'yes' then 1 else 0 end) as yes,
sum (case when test1= 'no' then 1 else 0 end) as no,
sum (case when test1 is null then 1 else 0 end) as notset
from names
group by Inspection1Type)
select
sum (yes) as 'yes',
sum(no) as 'no'
sum(notset) as 'Not Set'
from cte;
it works for the first column but not for the remaining ones as i'm looking for same value it complains about my aliases being the same

Try this cut&paste approach:
SELECT
sum (case when test1 = 'yes' then 1 else 0 end
+case when test2 = 'yes' then 1 else 0 end
+case when test3 = 'yes' then 1 else 0 end
...
+case when test10 = 'yes' then 1 else 0 end) as yes,
sum (case when test1 = 'no' then 1 else 0 end
+case when test2 = 'no' then 1 else 0 end
+case when test3 = 'no' then 1 else 0 end
...
+case when test10 = 'no' then 1 else 0 end) as no,
sum (case when test1 is null then 1 else 0 end
+case when test2 is null then 1 else 0 end
+case when test3 is null then 1 else 0 end
...
+case when test10 is null then 1 else 0 end) as notset
from names

I like handling this with apply. If you want one row per name:
select n.*, v.*
from names n cross apply
(select sum(case when v.test = 'yes' then 1 else 0 end) as yes,
sum(case when v.test = 'no' then 1 else 0 end) as no,
sum(case when v.test is null then 1 else 0 end) as nulls
from (values (n.test1), (n.test2), . . . (n.test10)) v(test)
) v;
If you want one row for all the data:
select sum(case when v.test = 'yes' then 1 else 0 end) as yes,
sum(case when v.test = 'no' then 1 else 0 end) as no,
sum(case when v.test is null then 1 else 0 end) as nulls
from names n cross apply
(values (n.test1), (n.test2), . . . (n.test10)) v(test);
Having repeating columns in a table is usually a sign of an issue with the data model. Normally, you would want one row per name/test, and that would simplify queries on this data.

Try this:
WITH ABC
as
(
SELECT 'bob' as name, 'yes' as test1, 'no' as test2, null as test3, 'yes' as test4
UNION ALL
SELECT 'john', 'no','yes','yes',null
)
SELECT SUM(CASE WHEN A.result = 'yes' then 1 else 0 end) as [Yes],
SUM(CASE WHEN A.result = 'no' then 1 else 0 end) as [No],
SUM(CASE WHEN A.result is null then 1 else 0 end) as [Null]
FROM
(
SELECT name,result
FROM ABC
CROSS APPLY(VALUES(test1),(test2),(test3),(test4)) --may add up to test10
COLUMNNAMES(result)
) as A

Related

Is there a way to rewrite this statement without sub queries?

I am trying to combine 4 queries for a monthly report so that I don't have to run them seperately. Our internal accounting software appears doesn't support sub queries so this statement doesn't work.
select left(salesgroupcode,4) as "Sales Group",
count(Number_of_products),
count(Number_of_discontinued),
count(Number_not_uploaded),
count(Number_sitting)
from (select
case when quantityavailable > 1 then 1 end Number_of_products,
case when quantityavailable > 1 and discontinued = true then 1 end Number_of_discontinued,
case when quantityavailable > 1 and z_datefirstuploaded is null then 1 end Number_not_uploaded,
case when quantityavailable > 1 and z_datefirstuploaded is null and dateoflastsale <= '01/01/2019' then 1 end Number_sitting
from icprod
) icprod
I don't have any other info about the specific rules etc that the software allows so I'm happy to try anything.
Thanks in advance.
Any help is appreciated
Yes, it is possible:
select left(salesgroupcode,4) as "Sales Group",
count(*),
count(case when discontinued = true then 1 end ),
count(case when z_datefirstuploaded is null then 1 end),
count(case when z_datefirstuploaded is null and dateoflastsale <= '01/01/2019')
from icprod
where quantityavailable > 1
group by left(salesgroupcode,4)
Just use conditional aggregation:
select sum(case when quantityavailable > 1 then 1 else 0 end ) as Number_of_products,
sum(case when quantityavailable > 1 and discontinued = true then 1 else 0 end) as Number_of_discontinued,
sum(case when quantityavailable > 1 and z_datefirstuploaded is null then 1 else 0 end) as Number_not_uploaded,
sum(case when quantityavailable > 1 and z_datefirstuploaded is null and dateoflastsale <= '2019-01-01' then 1 else 0 end) as Number_sitting
from icprod;
Notes that I changed the date constant to be ISO 8601 standard format. In some databases, you may need to precede that with date.
This can in turn be simplified to:
select count(*) as Number_of_products,
sum(case when discontinued = true then 1 else 0 end) as Number_of_discontinued,
sum(case when z_datefirstuploaded is null then 1 else 0 end) as Number_not_uploaded,
sum(case when z_datefirstuploaded is null and dateoflastsale <= '2019-01-01' then 1 else 0 end) as Number_sitting
from icprod
where quantityavailable > 1

Create and classify a row based on column values

I am attempting to assign a classification to a row of data based on whether certain values exist. Utilizing the sample code below I have gotten to a place where I've gotten stuck.
proc sql;
create table test
(id char(4),
task char(4),
id2 char(4),
status char(10),
seconds num);
insert into test
values('1','A','1','COMP',15)
values('1','B','2','WORK',20)
values('1','C','3','COMP',50)
values('1','D','3','COMP',null)
values('2','A','1','COMP',15)
values('2','B','2','COMP',520)
values('2','C','2','COMP',NULL)
values('2','D','3','COMP',221)
values('2','E','3','COMP',null)
values('2','F','3','COMP',null);
proc sql;
create table test2 as
select
ID,
ID2,
STATUS,
SUM(SECONDS) AS SECONDS,
sum(case when task='A' THEN 1 ELSE 0 END) AS A,
sum(case when task='B' THEN 1 ELSE 0 END) AS B,
sum(case when task='C' THEN 1 ELSE 0 END) AS C,
sum(case when task='D' THEN 1 ELSE 0 END) AS D,
sum(case when task='E' THEN 1 ELSE 0 END) AS E,
sum(case when task='F' THEN 1 ELSE 0 END) AS F
from
test
GROUP BY
ID,
ID2,
STATUS
;
quit;
Ultimately I would like to classify each row that gets created in the second step 'test2' to have a column that looks to the values in each lettered column(A-F) and Label them as such. So when the Row has a 1 in Column A only, it would be labeled 'A' but when a row has a 1 in multiple columns like 'D', 'E' and 'F' I would like it to be labeled as D_E_F.
Best way to do this is in a DATA STEP:
data test3;
format classifier $32.;
set test2;
array vars[6] A B C D E F;
classifier = "";
do i=1 to 6;
if vars[i] then
classifier = catx("_",classifier,vname(vars[i]));
end;
drop i;
run;
I create a character variable CLASSIFIER with length 32.
I define an array that groups the columns A through F. This allows me to loop over those columns easily.
Initialize the CLASSIFIER variable.
Loop over the array. If the value =1, then add the name of the variable to the CLASSIFIER string.
CATX(delim,str1,str2) concatenates str1 and str2 with the delim in the middle. It also removes whitespace.
VNAME(array[i]) returns the variable name of the variable pointed to by array[i].
Finally remove the i loop variable, unless you really want it in your output.
I know it is ugly, but you can do it with CASE statements accumulating the wanted result in another field. You have the SQL Fiddle here.
Note that if it is possible that the concatenation is empty you will have to check this condition to avoid performing the substring.
select
ID,
ID2,
STATUS,
SUM(SECONDS) AS SECONDS,
sum(case when task='A' THEN 1 ELSE 0 END) AS A,
sum(case when task='B' THEN 1 ELSE 0 END) AS B,
sum(case when task='C' THEN 1 ELSE 0 END) AS C,
sum(case when task='D' THEN 1 ELSE 0 END) AS D,
sum(case when task='E' THEN 1 ELSE 0 END) AS E,
sum(case when task='F' THEN 1 ELSE 0 END) AS F,
substring(
case when sum(case when task='A' THEN 1 ELSE 0 END) = 1 then '_A' else '' end
+ case when sum(case when task='B' THEN 1 ELSE 0 END) = 1 then '_B' else '' end
+ case when sum(case when task='C' THEN 1 ELSE 0 END) = 1 then '_C' else '' end
+ case when sum(case when task='D' THEN 1 ELSE 0 END) = 1 then '_D' else '' end
+ case when sum(case when task='E' THEN 1 ELSE 0 END) = 1 then '_E' else '' end
+ case when sum(case when task='F' THEN 1 ELSE 0 END) = 1 then '_F' else '' end,
2, len(case when sum(case when task='A' THEN 1 ELSE 0 END) = 1 then '_A' else '' end
+ case when sum(case when task='B' THEN 1 ELSE 0 END) = 1 then '_B' else '' end
+ case when sum(case when task='C' THEN 1 ELSE 0 END) = 1 then '_C' else '' end
+ case when sum(case when task='D' THEN 1 ELSE 0 END) = 1 then '_D' else '' end
+ case when sum(case when task='E' THEN 1 ELSE 0 END) = 1 then '_E' else '' end
+ case when sum(case when task='F' THEN 1 ELSE 0 END) = 1 then '_F' else '' end) - 1) as wantedOutput
from
test
GROUP BY
ID,
ID2,
STATUS

Why does this query have two selects?

I have this query :
SELECT WorkId, RegisterDate, sum(RoomType1) As RoomType1, sum(RoomType2) As RoomType2, sum(RoomType3) As RoomType3, sum(RoomType4) As RoomType4, sum(RoomType5) As RoomType5, sum(RoomType6) As RoomType6, sum(RoomType7) As RoomType7, sum(RoomType8) As RoomType8
FROM (
SELECT dbo.[Work].WorkId, dbo.[Work].RegisterDate,
case dbo.Floor.RoomType when 1 then 1 else 0 end as RoomType1,
case dbo.Kat.RoomType when 2 then 1 else 0 end as RoomType2,
FROM dbo.Belediye INNER JOIN
dbo.[Is] ON dbo.Municipality.MunicipalityId= dbo.[Is].MunicipalityWorkId INNER JOIN
dbo.Look ON dbo.[Work].LookWorkId = dbo.Look.LookId ,
WHERE (dbo.Look.LocationIS NOT NULL)
) E
GROUP BY WorkId,
This query works as expected, but I can't understand why it has two selects, why does it need them? Please explain it to me. Thanks.
As you suspected this query dont need two selects and could be rewritten without sub-query:
SELECT i.IsId,
i.KayitTarihi,
SUM(case k.OdaTipi when 1 then 1 else 0 end) as RoomType1,
SUM(case k.OdaTipi when 2 then 1 else 0 end) as RoomType2,
SUM(case k.OdaTipi when 3 then 1 else 0 end) as RoomType3,
SUM(case k.OdaTipi when 4 then 1 else 0 end) as RoomType4,
SUM(case k.OdaTipi when 5 then 1 else 0 end) as RoomType5,
SUM(case k.OdaTipi when 6 then 1 else 0 end) as RoomType6,
SUM(case k.OdaTipi when 7 then 1 else 0 end) as RoomType7,
SUM(case k.OdaTipi when 8 then 1 else 0 end) as RoomType8
FROM dbo.Belediye b
INNER JOIN dbo.[Is] i
ON b.BelediyeId = i.BelediyeIsId
INNER JOIN dbo.YerGorme yg
ON i.YerGormeIsId = yg.YerGormeId
INNER JOIN dbo.Kat k
ON yg.YerGormeId = k.YerGorme_YerGormeId
WHERE yg.Lokasyon IS NOT NULL
GROUP BY i.IsId, i.KayitTarihi
Note: use table aliases

Summing outputs from a case statement

I have a query like this:
SELECT
case [Group]
when 1 then 'in'
when 0 then 'out'
end as traffic
FROM [GW_Test_Back_Up].[dbo].[ARC_Calls_ReportView]
Which creates a new column with many rows containing in/out for traffic, but what I really want is only two rows, one for the sum of the in's and the other for the sum of the out's. I am stuck on how I would do this.
I do believe you are after:
SELECT
SUM(CASE WHEN [Group] = 1 then 1 ELSE 0 END ) AS InCount,
SUM(CASE WHEN [Group] = 0 then 1 ELSE 0 END ) AS OutCount
FROM [GW_Test_Back_Up].[dbo].[ARC_Calls_ReportView]
Or maybe this:
SELECT 'InCount' AS Type,
SUM(CASE WHEN [Group] = 1 then 1 ELSE 0 END ) AS InCount
FROM [GW_Test_Back_Up].[dbo].[ARC_Calls_ReportView]
UNION ALL
SELECT 'OutCount' AS Type,
SUM(CASE WHEN [Group] = 0 then 1 ELSE 0 END ) AS OutCount
FROM [GW_Test_Back_Up].[dbo].[ARC_Calls_ReportView]
EDIT:
SELECT
CASE WHEN m.InCount > 10 THEN 'High' ELSE 'Low' END AS InCountStatus
CASE WHEN m.OutCount > 10 THEN 'High' ELSE 'Low' END AS OutCountStatus
FROM
(
SELECT
SUM(CASE WHEN [Group] = 1 then 1 ELSE 0 END ) AS InCount,
SUM(CASE WHEN [Group] = 0 then 1 ELSE 0 END ) AS OutCount
FROM [GW_Test_Back_Up].[dbo].[ARC_Calls_ReportView]
) m

Count of non Null values in a Row

Is there a better way of doing this?
select count(First)+count(id)+count(Last)+count(Telephone)
from client
where id ="1";
Not sure if this is better, but the intent may be clearer to someone reading it later:
select
(case when First is null then 1 else 0 end) +
(case when id is null then 1 else 0 end) +
(case when Last is null then 1 else 0 end) +
(case when Telephone is null then 1 else 0 end)
from client
where id ="1";