Filling in the missing field using SQL - sql

This is actually related to the question I asked previously: Filling in the missing field for column in data frame in R
I have the following columns:
casenum boxtype numballs
1 A 10
1 B 20
2 B 1
2 C 2
2 D 12
3 A 10
3 B 20
3 C 1
3 D 2
. . .
. . .
. . .
I want to have it in a format
casenum A B C D
1 10 20 0 0
2 0 1 2 12
3 10 20 1 2
. . . . .
. . . . .
I have learned how to do so in R.
Then I was wondering if I could do the similar thing in SQL.
It looks like the query should end with "group by casenum, boxtype," but then I wasn't sure how to make those 4 new columns using SQL.
Is there an easy way of doing it in SQL?

This is a pivot query. A general way to solve it uses conditional aggregation:
select casenum,
sum(case when boxtype = 'A' then numballs else 0 end) as A,
sum(case when boxtype = 'B' then numballs else 0 end) as B,
sum(case when boxtype = 'C' then numballs else 0 end) as C,
sum(case when boxtype = 'D' then numballs else 0 end) as D
from t
group by casenum;

If you are using SQL Server 2005+, then you can use the PIVOT function to transform the data from rows into columns.
If your values(boxtype) are known, then you will hard-code the query:
select *
from
(
select casenum, boxtype, numballs
from #table
) src
pivot
(
sum(numballs)
for week in ([A], [B], [C],[D])
) piv;
if you need to generate the boxtype dynamically, your code will be:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(boxtype)
from table
group by boxtype
order by boxtype
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT casenum,' + #cols + ' from
(
select casenum, boxtype, numballs
from table
) x
pivot
(
sum(numballs)
for boxtype in (' + #cols + ')
) p '
execute(#query);

Related

Count grouped values

Again I need some help.
I have a table (for the sake of simplicity) with 3 fields.
code id letter
1 2016 Pablo A
2 2017 Pablo B
3 2016 Ana B
4 2017 Pablo A
5 2018 Ana A
6 2018 Ana A
I need a query that results in
code id letterA letterB
1 2016 Pablo 1 Null
2 2017 Pablo 1 1
3 2016 Ana Null 1
4 2018 Ana 2 Null
As you can see I count the records for id and grouped by code, if they have different letters for code a new record appears, but if they have both letters on the same code is just one record.
I tried with UNION but what I got is two records (with the same code) with different letters.
Thanks guys,
Edit one:
The query with union
select code, id, count(id), 'letter A' letter
from table
where letter = 'A'
union
select code, id, count(id), 'letter B' letter
from table
where letter = 'B'
I got something like this
code id count(id) letter
1 2016 Pablo 1 A
2 2017 Pablo 1 A
3 2017 Pablo 1 B
4 2016 Ana 1 B
5 2018 Ana 2 A
The problem is that I have 2 code 2017 with id Pablo, I would like to have just 1
You almost got it. You only need another GROUP BY to get the result that you wanted.
Using PIVOT
select *
from tbl t
pivot
(
count(letter)
for letter in ([A], [B])
) p
order by id desc, code
Using Union All
select code, id, A = sum(A), B = sum(B)
from
(
select code, id, A = count(*), B = null
from tbl t
where letter = 'A'
group by code, id
union all
select code, id, A = null, B = count(*)
from tbl t
where letter = 'B'
group by code, id
) d
group by code, id
order by id desc, code
You can do this by executing a dynamic sql query rather than giving values explicitly.
Query
declare #sql as varchar(max);
select #sql = 'select [code], [id], ' + stuff((
select distinct ', sum(case [letter] when ' + char(39) + [letter] + char(39)
+ ' then 1 else 0 end) as [letter' + [letter] + '] '
from [dbo].[your_table_name]
for xml path('')
)
, 1, 2, ''
);
select #sql += ' from [dbo].[your_table_name] group by [code], [id] order by [id];';
exec(#sql);
Other approach is using CASE expression in SELECT by grouping rows.
select code,
id,
SUM(CASE WHEN letter= 'A' THEN 1 ELSE 0 END) AS 'letter A' ,
SUM(CASE WHEN letter= 'B' THEN 1 ELSE 0 END) AS 'letter B'
from table
group by code, id
Note: If there are no letters, then it returns 0 instead of NULL.

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

Count in SQL statement

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

Aggregate function within inner select in Pivot query using SQL Server

I have the following table:
select * from product;
slno item
---------------
1 HDD
2 PenDrive
3 RAM
4 DVD
5 RAM
6 HDD
7 RAM
7 RAM
7 RAM
Now I need to do pivoting for this table for which i am using following query:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(item)
from product
group by item
order by item
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT slno,TotalProduct ,' + #cols + '
from
(
select slno,Count(*) as TotalProduct,item
from product
group by slno,item
) x
pivot
(
count(item)
for item in (' + #cols + ')
) p '
exec(#query)
Result:
slno TotalProducts DVD HDD PenDrive RAM
---------------------------------------------
1 1 0 1 0 0
2 1 0 0 1 0
3 1 0 0 0 1
4 1 1 0 0 0
5 1 0 0 0 1
6 1 0 1 0 0
7 3 0 0 0 1
Note The total of product RAM is 3 but in Column RAM showing only 1. I have used COUNT(*) aggregate function within the inner select statement in #query. How can i show actual count?
You only need to group by slno, not by the combination of slno and item. Therefore, you need to change the query which provides a source for your pivot as follows:
set #query = 'SELECT slno,totalproduct,' + #cols + '
from
(
select p.slno slno, c.count as totalproduct, p.item
from product p
inner join
(select slno, count(item) count
from product
group by slno) c on p.slno = c.slno
) x
pivot
(
count(item)
for item in (' + #cols + ')
) p '
Demo
Use following sub query instead of your sub query:
select slno,Count(*) OVER (PARTITION BY slno) as TotalProduct,item
from product
Edit: Count(*) Over(Partition by ...) supported in SQL Server 2012 and above versions.

How to write a query table value as column name?

My Sql table is similar like below
Code Value ID
A 100 1
A 200 2
A 300 3
B 200 1
B 500 2
B 600 3
C 800 1
C 700 2
C 200 3
How I can write query in sql server 2008 to get values in below format.
ID A B C
1 100 200 800
2 200 500 700
3 300 600 200
You can use SUM function for that:
SELECT ID,
SUM(CASE Code when 'A' then Value else 0 end)as A,
SUM(CASE Code when 'B' then Value else 0 end)as B,
SUM(CASE Code when 'C' then Value else 0 end)as C
FROM myTable
GROUP BY ID;
See this SQLFiddle
Use PIVOT
select ID,[A],[B],[C]
from your_table T
PIVOT (MAX(Value) FOR Code in ([A],[B],[C]) )P
IF the number if Codes are not fixed you could use dynamic pivot
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(Code)
from your_table
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ID, ' + #cols + '
from your_table
pivot
(
MAX([Value])
for Code in (' + #cols + ')
) p '
print(#query)
execute(#query)
The answer is PIVOT
DECLARE #t TABLE (Code varchar(10), Value int, Id int)
INSERT INTO #t VALUES
('A',100,1),
('A',200,2),
('A',300,3),
('B',200,1),
('B',500,2),
('B',600,3),
('C',800,1),
('C',700,2),
('C',200,3);
SELECT ID,[A],[B],[C]
FROM #t
PIVOT (SUM(Value) FOR Code IN ([A],[B],[C]))P
Result
ID A B C
1 100 200 800
2 200 500 700
3 300 600 200