Count in SQL statement - sql

I have a table with the following data:
Comp ID Name Type
-----------------------
AAA D2222 Jon BR11
AAA D2222 Jon BR12
AAA D2865 Toe BR11
BBB D4151 Sue BR11
BBB D4151 Sue BR12
BBB D4151 Sue BR13
CCC D6080 Pete BR14
CCC D6723 Tom BR13
I want to write my SQL statement and display like table below
Comp BR11 BR12 BR13 BR14
---------------------------
AAA 2 1
BBB 1 1 1
CCC 1 1
But I only know to select for one Type, how can I do it for many Types ?
select
Comp, count(Type) as BR11
from
CCDL
where
Type = 'BR11'
group by
Comp
Thanks much !

Dynamic pivot is the best approach:
create table test (Comp varchar(3), ID varchar(10), Name varchar(10), Type varchar(10))
insert into test values ('AAA','D2222','Jon','BR11');
insert into test values ('AAA','D2222','Jon','BR12');
insert into test values ('AAA','D2865','Toe','BR11');
insert into test values ('BBB','D4151','Sue','BR11');
insert into test values ('BBB','D4151','Sue','BR12');
insert into test values ('BBB','D4151','Sue','BR13');
insert into test values ('CCC','D6080','Pete','BR14');
insert into test values ('CCC','D6723','Tom','BR13');
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.Type)
FROM test c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Comp, ' + #cols + ' from
(
select Comp
, ID
, Type
from test
) x
pivot
(
count(ID)
for Type in (' + #cols + ')
) p '
execute(#query)
The result is
Comp BR11 BR12 BR13 BR14
AAA 2 1 0 0
BBB 1 1 1 0
CCC 0 0 1 1

You can use selective aggregates for this:
SELECT Comp
, COUNT(CASE WHEN type = 'BR11' THEN 1 END) br11
, COUNT(CASE WHEN type = 'BR12' THEN 1 END) br12
, ...
FROM CCDL
GROUP BY Comp
More about this: http://modern-sql.com/feature/filter
It's basically also a pivot technique: http://modern-sql.com/use-case/pivot

try the following code
declare #tab table (Comp varchar(50),Id varchar(50),Name varchar(50),Type varchar(50))
insert into #tab
Select 'AAA','D2222','Jon','BR11' Union ALL
Select 'AAA','D2222','Jon','BR12' Union ALL
Select 'AAA','D2865','Toe','BR11' Union ALL
Select 'BBB','D4151','Sue','BR11' Union ALL
Select 'BBB','D4151','Sue','BR12' Union ALL
Select 'BBB','D4151','Sue','BR13' Union ALL
Select 'CCC','D6080','Pete','BR14'Union ALL
Select 'CCC','D6723','Tom','BR13'
Select * from
(Select type,comp,count(*) cnt from #tab
group by type,Comp
)d
PIVOT
(Sum(Cnt) FOR Type in ([BR11],[BR12],[BR13],[BR14]))p

TRY THIS : Use CASE with SUM as below:
SELECT Comp,
SUM(CASE WHEN type = 'BR11' THEN 1 ELSE 0 END) br11,
SUM(CASE WHEN type = 'BR12' THEN 1 ELSE 0 END) br12,
SUM(CASE WHEN type = 'BR13' THEN 1 ELSE 0 END) br13,
SUM(CASE WHEN type = 'BR14' THEN 1 ELSE 0 END) br14
FROM CCDL
GROUP BY Comp

What you can do
SELECT Comp,
SUM(CASE WHEN type = 'BR11' THEN 1 ELSE 0 END) br11,
SUM(CASE WHEN type = 'BR12' THEN 1 ELSE 0 END) br12,
SUM(CASE WHEN type = 'BR13' THEN 1 ELSE 0 END) br13,
SUM(CASE WHEN type = 'BR14' THEN 1 ELSE 0 END) br14
FROM CCDL
GROUP BY Comp

Related

SQL - Transform Symbol's Column Values Into Columns with Symbols as Names and Type of Boolean

I have this table with the Symbol column:
I need to transform it into a table which has the Symbol's column values (distinct) as new Column Name. These columns should be of Type bool or INT (0/1). Resulting table should look like this below:
P.S. What if the set of columns was dynamically generated by another query?
If you need dynamic Columns, you would need Dynamic SQL
Example
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(Symbol)+'=case when symbol='''+Symbol+''' then 1 else 0 end ' From Yourtable Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select '+#SQL+'
,PartOfDay
,isProfitable
From YourTable
'
Exec(#SQL)
Returns
Just use case:
select (case when symbol = 'ACRX' then 1 else 0 end) as acrx,
(case when symbol = 'ANF' then 1 else 0 end) as anf,
(case when symbol = 'NVDA' then 1 else 0 end) as nvda,
(case when symbol = 'rsx' then 1 else 0 end) as rsx,
partofday, isprofitable
from t;
you can do
select
IIF('ACRX', 1, 0) as acrx,
IIF('ANF', 1, 0) as anf,
IIF('NVDA', 1, 0) as nvda,
IIF('RXS', 1, 0) as rsx,
partofday,
isprofitable
from t;
You could also apply pivot operator to the dynamic SQL.
For example:
create table test03
(
Symbol varchar(30),
PartOfDay int,
isProfitable int
)
insert into test03 values
('ACRX',3,0),
('ACRX',3,0),
('ANF',2,1),
('ANF',2,1),
('ANF',2,1),
('NVDA',3,1),
('RSX',3,0),
('RSX',3,0)
--Query
DECLARE #V_COLUMNS VARCHAR(MAX)
DECLARE #V_SQL VARCHAR(MAX)
SET #V_COLUMNS=STUFF((SELECT DISTINCT ','+QUOTENAME(Symbol) FROM test03 FOR XML PATH('')),1,1,'')
--print #V_COLUMNS
SET #V_SQL='
;WITH CTE AS
(
select
ROW_NUMBER() over (order by Symbol) as RN,
Symbol,
1 AS V_VALUE,
PartOfDay,
isProfitable
from test03
)
SELECT '+#V_COLUMNS+',PartOfDay,isProfitable
FROM CTE
PIVOT
(
COUNT(V_VALUE) FOR Symbol IN ('+#V_COLUMNS+')
) PVT'
--PRINT #V_SQL
EXEC(#V_SQL)
--Output
/*
ACRX ANF NVDA RSX PartOfDay isProfitable
----------- ----------- ----------- ----------- ----------- ------------
1 0 0 0 3 0
1 0 0 0 3 0
0 1 0 0 2 1
0 1 0 0 2 1
0 1 0 0 2 1
0 0 1 0 3 1
0 0 0 1 3 0
0 0 0 1 3 0
*/

What is the SQL code for aggregating values?

I have the following table:
GR WORD NO.
1 A 4
2 B 5
3 C 6
1 G 5
2 H 5
3 I 5
I would like to get the following table:
GR 4 5 6
1 1 1 0
2 0 2 0
3 0 1 1
For each GR column value I count the NO. values.
Here's a dynamic solution:
--Sample data
--CREATE TABLE tbl (GR int, WORD char(1), [NO] int)
--INSERT INTO tbl values
--(1,'A',4),
--(2,'B',5),
--(3,'C',6),
--(1,'G',5),
--(2,'H',5),
--(3,'I',5)
DECLARE #sql NVARCHAR(MAX)
SELECT #sql = '
SELECT *
FROM tbl
PIVOT(
COUNT(WORD) FOR [NO] IN (' +
(SELECT STUFF(
(
SELECT DISTINCT ',' + QUOTENAME(CAST([NO] AS VARCHAR(10)))
FROM tbl
FOR XML PATH('')
)
, 1, 1, ''))
+ ')
) p
'
EXEC sp_executesql #sql
This is a conditional aggregation
select
GR
,[4] = count(case when NO. = 4 then WORD end)
,[5] = count(case when NO. = 5 then WORD end)
,[6] = count(case when NO. = 6 then WORD end)
from YourTable
group by GR
Or a pivot
select *
from YourTable
pivot(
count(WORD) for NO. in ([4],[5],[6])
) p

Calculating averages in SQL Server without ignoring null values

I have a query like below:
DECLARE #t TABLE
(
EmpName VARCHAR(10)
, Qty INT
, Item VARCHAR(12)
)
INSERT INTO #t
VALUES ('Jane',3,'Dog')
, ('Carle',1,'Cat')
, ('Abay',5,'Goat')
, ('Jane',1,'Dog')
, ('Carle',10,'Cat')
, ('Jane',2,'Dog')
, ('Jane',8,'Goat')
, ('Jane',3,'Ram')
, ('Carle',2,'Dog')
--SELECT * FROM #t
SELECT
EmpName, [Dog], [Cat], [Goat], [Ram]
FROM
(SELECT
EmpName, Qty, Item
FROM #t) AS b
PIVOT(SUM(Qty) FOR Item IN ([Dog], [Cat], [Goat], [Ram])) AS p
And the result is as seen in the screenshot below:
I want to calculate the average Qty across Item without ignoring null values in the calculation. For example, in row 1, EmpName Abay should be 5 divided by 4 (number of columns), as seen in this screenshot:
How do I get the average column?
I'm not really familiar with the PIVOT query, so here is an alternative using conditional aggregation:
SELECT
Empname,
Dog = SUM(CASE WHEN Item = 'Dog' THEN Qty ELSE 0 END),
Cat = SUM(CASE WHEN Item = 'Cat' THEN Qty ELSE 0 END),
Goat = SUM(CASE WHEN Item = 'Goat' THEN Qty ELSE 0 END),
Ram = SUM(CASE WHEN Item = 'Ram' THEN Qty ELSE 0 END),
Average = SUM(ISNULL(Qty, 0))/ 4.0
FROM #t
GROUP BY EmpName;
Note that this will only work if you only have 4 Items. Otherwise, you need to resort to dynamic crosstab.
ONLINE DEMO
For dynamic crosstab, I used a temporary table instead of a table variable:
DECLARE #sql NVARCHAR(MAX) = '';
SELECT #sql =
'SELECT
Empname' + CHAR(10);
SELECT #sql = #sql +
' , SUM(CASE WHEN Item = ''' + Item + ''' THEN Qty ELSE 0 END) AS ' + QUOTENAME(Item) + CHAR(10)
FROM (
SELECT DISTINCT Item FROM #t
) t;
SELECT #sql = #sql +
' , SUM(ISNULL(Qty, 0)) / (SELECT COUNT(DISTINCT Item) * 1.0 FROM #t) AS [Average]' + CHAR(10) +
'FROM #t
GROUP BY EmpName;';
ONLINE DEMO
Try a combination of AVG and ISNULL, i.e. AVG(ISNULL(Dog, 0)).
One simple method is:
select empname, goat, cat, dog, ram,
(coalesce(goat, 0) + coalesce(cat, 0) + coalesce(dog, 0) + coalesce( ram, 0)
) / 4.0 as average
from t;
Another simple method uses outer apply:
select t.*, v.average
from t outer apply
(select avg(coalesce(x, 0))
from (values (t.goat), (t.cat), (t.dog), (t.ram)
) v(x)
) v(average);
DECLARE #t TABLE
(
EmpName VARCHAR(10)
, Qty INT
, Item VARCHAR(12)
)
INSERT INTO #t
VALUES ('Jane',3,'Dog')
, ('Carle',1,'Cat')
, ('Abay',5,'Goat')
, ('Jane',1,'Dog')
, ('Carle',10,'Cat')
, ('Jane',2,'Dog')
, ('Jane',8,'Goat')
, ('Jane',3,'Ram')
, ('Carle',2,'Dog')
SELECT EmpName
, [Dog]
, [Cat]
, [Goat]
, [Ram]
,p.total/4.0 as av
FROM (SELECT EmpName, Qty, Item,SUM(qty)OVER(PARTITION BY EmpName) AS total FROM #t) AS b
PIVOT(SUM(Qty) FOR Item IN([Dog],[Cat],[Goat],[Ram])) AS p
EmpName Dog Cat Goat Ram av
---------- ----------- ----------- ----------- ----------- ---------------------------------------
Abay NULL NULL 5 NULL 1.250000
Carle 2 11 NULL NULL 3.250000
Jane 6 NULL 8 3 4.250000
V2: Dynamic script:
CREATE TABLE #t
(
EmpName VARCHAR(10)
, Qty INT
, Item VARCHAR(12)
)
INSERT INTO #t
VALUES ('Jane',3,'Dog')
, ('Carle',1,'Cat')
, ('Abay',5,'Goat')
, ('Jane',1,'Dog')
, ('Carle',10,'Cat')
, ('Jane',2,'Dog')
, ('Jane',8,'Goat')
, ('Jane',3,'Ram')
, ('Carle',2,'Dog')
INSERT #t ( EmpName, Qty, Item )VALUES('Abay',100,'abc')
DECLARE #cols VARCHAR(max),#sql VARCHAR(MAX),#cnt INT
SELECT #cols=ISNULL(#cols+',[','[')+Item+']',#cnt=ISNULL(#cnt+1,1) FROM #t GROUP BY Item
PRINT #cols
PRINT #cnt
SET #sql='SELECT EmpName, '+#cols+',p.total*1.0/'+LTRIM(#cnt)+' as av'+CHAR(13)
+' FROM (SELECT EmpName, Qty, Item,SUM(qty)OVER(PARTITION BY EmpName) AS total FROM #t) AS b'+CHAR(13)
+' PIVOT(SUM(Qty) FOR Item IN('+#cols+')) AS p'
EXEC(#sql)
EmpName abc Cat Dog Goat Ram av
---------- ----------- ----------- ----------- ----------- ----------- ---------------------------------------
Carle NULL 11 2 NULL NULL 2.600000
Jane NULL NULL 6 8 3 3.400000
Abay 100 NULL NULL 5 NULL 21.000000
Avoid NULL from your pivot sentence and compute AVG.
;with ct as
(
SELECT EmpName
, ISnull([Dog],0) Dog
, ISnull([Cat],0) Cat
, ISnull([Goat],0) Goat
, ISnull([Ram],0) Ram
FROM (SELECT EmpName, Qty, Item FROM #t) AS b
PIVOT(SUM(Qty) FOR Item IN([Dog],[Cat],[Goat],[Ram])) AS p
)
select empname, avg(dog) dog, avg(cat) cat, avg(goat) goat, avg(ram) ram
from ct
group by empname;
+---------+-----+-----+------+-----+
| empname | dog | cat | goat | ram |
+---------+-----+-----+------+-----+
| Abay | 0 | 0 | 5 | 0 |
+---------+-----+-----+------+-----+
| Carle | 2 | 11 | 0 | 0 |
+---------+-----+-----+------+-----+
| Jane | 6 | 0 | 8 | 3 |
+---------+-----+-----+------+-----+
SELECT EmpName
, [Dog]
, [Cat]
, [Goat]
, [Ram]
,(isnull(p.cat,0)+isnull(p.dog,0)+isnull(p.Goat,0)+isnull(p.Ram,0))/4.0 as average
FROM (SELECT EmpName, Qty, Item FROM #t) AS b
PIVOT(SUM(Qty) FOR Item IN([Dog],[Cat],[Goat],[Ram])) AS p

SQL Multiple count on same row with dynamic column

I need to alter view that show user count(ScheduleID) by period on same row. Now the Period table content can grow and contain more than 3 periods.
The actual SQL is:
SELECT r.Code,
SUM(CASE WHEN s.PeriodID=1 THEN 1 ELSE 0 END) AS PeriodID1,
SUM(CASE WHEN s.PeriodID=2 THEN 1 ELSE 0 END) AS PeriodID2,
SUM(CASE WHEN s.PeriodID=3 THEN 1 ELSE 0 END) AS PeriodID3,
SUM(CASE WHEN s.PeriodID IN (1,2,3) THEN 1 ELSE 0 END) AS Total
FROM Schedules s
JOIN Periods p ON p.PeriodID = s.PeriodID
JOIN Resources r ON r.ResourceID = s.ResourceID
GROUP BY r.Code;
Example data:
Table Schedules
ScheduleID(int) ResourceID(int) ResourceCode(varchar 4) PeriodID(int)
1 1 AA 1
2 1 AA 3
3 1 AA 3
4 2 BB 1
5 3 CC 1
6 1 AA 1
7 3 CC 2
8 3 CC 3
9 2 BB 1
10 2 BB 2
11 2 BB 3
12 1 AA 3
Table Periods
PeriodID(int) Code (varchar 4)
1 P1
2 P2
3 P3
4 P4
5 P5
6 P6
7 P7
8 P8
The result I need is:
ResourceCode PeriodID1 PeriodID2 PeriodID3 ... PeriodID8 TOTAL
AA 2 0 3 0 5
BB 2 1 1 0 4
CC 1 1 1 0 3
The Periods table content is now dynamic.
The database version is an Microsoft SQL 2008
I like to know if is possible to do that without create stored procedure...and doing this in one query like this:
SELECT *
FROM (
SELECT R.Code, P.PeriodID, COUNT(S.ScheduleID) AS RPCount
FROM Schedules S INNER JOIN Periods P ON S.PeriodID = P.PeriodID
JOIN Resources R ON S.ResourceID = R.ResourceID
WHERE S.ResourceID is not null
GROUP BY R.Code, P.PeriodID
) as data
PIVOT
(
SUM(RPCount)
--FOR PeriodID IN ([1],[2],[3])
FOR PeriodID IN (SELECT PeriodID From Periods)
)AS pvt
ORDER BY Code
Since you are using SQL Server then you can implement the PIVOT function and if you have an unknown number of period values, then you will need to use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME('PeriodId'+cast(periodid as varchar(10)))
from Periods
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT resourcecode, ' + #cols + ' , Total
from
(
select s.resourcecode,
''PeriodId''+cast(p.periodid as varchar(10)) period,
count(*) over(partition by s.resourcecode) Total
from periods p
left join schedules s
on p.periodid = s.periodid
) x
pivot
(
count(period)
for period in (' + #cols + ')
) p
where resourcecode is not null
order by resourcecode'
execute(#query)
See SQL Fiddle with Demo. This gives a result:
| RESOURCECODE | PERIODID1 | PERIODID2 | PERIODID3 | PERIODID4 | PERIODID5 | PERIODID6 | PERIODID7 | PERIODID8 | TOTAL |
------------------------------------------------------------------------------------------------------------------------
| AA | 2 | 0 | 3 | 0 | 0 | 0 | 0 | 0 | 5 |
| BB | 2 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 4 |
| CC | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 3 |
Based on your previous question that was tagged with MySQL, I am assuming you are using MySQL as the database. If so, then you do not have a PIVOT function so you will have to use an aggregate function with a CASE expression to transform the rows of data into columns.
If your column values are known, then you can hard-code the query:
select resourcecode,
sum(case when period = 'PeriodId1' then 1 else 0 end) PeriodId1,
sum(case when period = 'PeriodId2' then 1 else 0 end) PeriodId2,
sum(case when period = 'PeriodId3' then 1 else 0 end) PeriodId3,
sum(case when period = 'PeriodId4' then 1 else 0 end) PeriodId4,
sum(case when period = 'PeriodId5' then 1 else 0 end) PeriodId5,
sum(case when period = 'PeriodId6' then 1 else 0 end) PeriodId6,
sum(case when period = 'PeriodId7' then 1 else 0 end) PeriodId7,
sum(case when period = 'PeriodId8' then 1 else 0 end) PeriodId8,
count(*) Total
from
(
select concat('PeriodId', p.periodid) Period,
s.resourcecode
from periods p
left join schedules s
on p.periodid = s.periodid
) d
where resourcecode is not null
group by resourcecode;
See SQL Fiddle with Demo. But if the values will be unknown or dynamic then you will need to use a prepared statement to generate a sql string to execute:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'sum(CASE WHEN period = ''',
concat('PeriodId', periodid),
''' THEN 1 else 0 END) AS `',
concat('PeriodId', periodid), '`'
)
) INTO #sql
FROM periods;
SET #sql
= CONCAT('SELECT resourcecode, ', #sql, ' , count(*) Total
from
(
select concat(''PeriodId'', p.periodid) Period,
s.resourcecode
from periods p
left join schedules s
on p.periodid = s.periodid
) d
where resourcecode is not null
group by resourcecode');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo.
Use PIVOT
try this
SELECT *
FROM (
SELECT
S.ResourceCode ,
P.PeriodID AS period,
COUNT(*) AS PCount
FROM Schedules S INNER JOIN Periods P ON S.PeriodID =P.PeriodID
GROUP BY S.ResourceCode ,P.PeriodID
) as s
PIVOT
(
PCount,
FOR [period] IN (SELECT DISTINCT PeriodID From Periods)
)AS pivot
Please try below code for MS Sql server:
DECLARE #column VARCHAR(MAX), #SumQuery VARCHAR(MAX)
SELECT
#column = COALESCE(#column + '], [', '')+ CAST(PeriodID as nvarchar(10)),
#SumQuery = COALESCE(#SumQuery + ']+[', '')+ CAST(PeriodID as nvarchar(10))
FROM
Periods
GROUP BY PeriodID
EXEC ('select *, ['+#SumQuery+'] as [Total] From
(
select * From Schedules
)up
pivot (count(ScheduleID) for PeriodID in (['+#column+'])) as pvt')

How can I return several text fields with the same ID number in one row in SQL server?

I have table:
ID Note
1 1 aaa
2 1 bbb
3 1 ccc
4 2 ddd
5 2 eee
6 2 fff
I need to return it as:
ID Note1 Note2 Note3
1 1 aaa bbb ccc
2 2 ddd eee fff
Thank you!
You can use the PIVOT function for this type of query. If you have a known number of columns, then you can hard-code the values:
select *
from
(
select id, note,
'Note' +
cast(row_number() over(partition by id order by id) as varchar(10)) col
from yourtable
) x
pivot
(
max(note)
for col in ([Note1], [Note2], [Note3])
) p
See SQL Fiddle with Demo
If you are going to have an unknown number of notes that you want to turn into columns, then you can use dynamic sql:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ','
+ QUOTENAME('Note' +
cast(row_number() over(partition by id order by id) as varchar(10)))
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id,' + #cols + ' from
(
select id, note,
''Note'' +
cast(row_number() over(partition by id order by id) as varchar(10)) col
from yourtable
) x
pivot
(
max(note)
for col in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
Both will produce the same results.
| ID | NOTE1 | NOTE2 | NOTE3 |
------------------------------
| 1 | aaa | bbb | ccc |
| 2 | ddd | eee | fff |
Or if you do not want to use the PIVOT function, then you can use an aggregate function with a CASE statement:
select id,
max(case when rn = 1 then note else '' end) Note1,
max(case when rn = 2 then note else '' end) Note2,
max(case when rn = 3 then note else '' end) Note3
from
(
select id, note,
row_number() over(partition by id order by id) rn
from yourtable
) src
group by id
See SQL Fiddle with Demo