How to write a query table value as column name? - sql

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

Related

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

Create a sparse matrix from observations

I am new to SQL server, I am a R user but R can't be used with my huge dataset (not enough memory).
What I want to do:
I want to create a sparse matrix from a table with only 2 columns, I dont have any value column, it seems easy but I didn't find the right way to do it.
My data:
ID_patient | ID_product
-----------------------
123 A
123 B
111 C
222 A
333 D
333 E
Ouput wanted:
ID_patient | A | B | C | D | E |
----------------------------------------------------
123 1 1
111 1
222 1
333 1 1
I have read that I can use the GROUP BY function or Pivot feature but what I have tried so far failed.
Edit
I don't know all the products, so the right way to do that is by using dynamic pivot ?
You can try something like PIVOT
See demo
Select * from
(select *, copy=Id_product from t)t
pivot
(
count(copy) for ID_product in ([A],[B],[C],[D],[E]))p
If you don't know A, B, C, D, .. before hand then you should go for dynamic pivot
update:
updated dynamic piv demo
declare #cols nvarchar(max)
declare #query nvarchar(max)
select #cols= Stuff((select ','+ quotename( ID_product) from
(select distinct id_product from t) t for xml path ('')),1,1,'')
select #query='Select * from
( select *, copy=Id_product from t ) t
pivot
(count(copy) for ID_product in ( '+#cols+' ))p '
exec(#query)
Try this
IF OBJECT_ID('tempdb..#Temp')IS NOT NULL
DROP TABLE #Temp
DECLARE #temp AS TABLE (ID_patient INT, ID_product varchar(10))
INSERT INTO #temp
SELECT 123,'A' UNION ALL
SELECT 123,'B' UNION ALL
SELECT 111,'C' UNION ALL
SELECT 222,'A' UNION ALL
SELECT 333,'D' UNION ALL
SELECT 333,'E'
SELECT * INTO #Temp
FROM #temp
DECLARE #Sql nvarchar(max),
#Col nvarchar(max),
#Col2 nvarchar(max)
SELECT #Col=STUFF((SELECT DISTINCT ', '+QUOTENAME(ID_product) FROM #Temp FOR XML PATH ('')),1,1,'')
SELECT #Col2=STUFF((SELECT DISTINCT ', '+'ISNULL('+QUOTENAME(ID_product)+','' '') AS '+QUOTENAME(ID_product) FROM #Temp FOR XML PATH ('')),1,1,'')
SET #Sql='
SELECT ID_patient,'+#Col2+'
FROM
(
SELECT *, DENSE_RANK()OVER( PARTITION BY ID_patient ORDER By ID_patient) AS [Val] FROM #Temp
)AS Src
PIVOT
(MAX(Val) FOR ID_product IN ('+#Col+')
)AS PVT '
PRINT #Sql
EXEC (#Sql)
Result
ID_patient A B C D E
------------------------------
111 0 0 1 0 0
123 1 1 0 0 0
222 1 0 0 0 0
333 0 0 0 1 1

Filling in the missing field using 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);

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.

Dynamic Pivot (row to columns)

I have a Table1:
ID Instance Name Size Tech
1 0 D1 123 ABC
1 1 D2 234 CDV
2 2 D3 234 CDV
2 3 D4 345 SDF
I need the resultset using Dynamic PIVOT to look like along with the headers:
ID | Instance0_Name | Instance0_Size | Instance0_Tech | Instance1_Name | Instance1_Size | Instance1_tech
1 | D1 | 123 | ABC | D2 | 234 | CDV
Any help would be appreciated. using Sql Server 2008.
Sorry for the earlier post.
Your desired output is not exactly clear, but you can use the both the UNPIVOT and PIVOT function to get the result
If you know the number of columns, then you can hard code the values:
select *
from
(
select id,
'Instance'+cast(instance as varchar(10))+'_'+col col,
value
from
(
select id,
Instance,
Name,
cast(Size as varchar(50)) Size,
Tech
from yourtable
) x
unpivot
(
value
for col in (Name, Size, Tech)
) u
) x1
pivot
(
max(value)
for col in
([Instance0_Name], [Instance0_Size], [Instance0_Tech],
[Instance1_Name], [Instance1_Size], [Instance1_Tech],
[Instance2_Name], [Instance2_Size], [Instance2_Tech],
[Instance3_Name], [Instance3_Size], [Instance3_Tech])
) p
See SQL Fiddle with Demo
Then if you have an unknown number of values, you can use dynamic sql:
DECLARE #query AS NVARCHAR(MAX),
#colsPivot as NVARCHAR(MAX)
select #colsPivot = STUFF((SELECT ','
+ quotename('Instance'+ cast(instance as varchar(10))+'_'+c.name)
from yourtable t
cross apply sys.columns as C
where C.object_id = object_id('yourtable') and
C.name not in ('id', 'instance')
group by t.instance, c.name
order by t.instance
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'select *
from
(
select id,
''Instance''+cast(instance as varchar(10))+''_''+col col,
value
from
(
select id,
Instance,
Name,
cast(Size as varchar(50)) Size,
Tech
from yourtable
) x
unpivot
(
value
for col in (Name, Size, Tech)
) u
) x1
pivot
(
max(value)
for col in ('+ #colspivot +')
) p'
exec(#query)
See SQL Fiddle with Demo
If the result is not correct, then please edit your OP and post the result that you expect from both of the Ids you provided.