Item table:
| Item | Qnty | ProdSched |
| a | 1 | 1 |
| b | 2 | 1 |
| c | 3 | 1 |
| a | 4 | 2 |
| b | 5 | 2 |
| c | 6 | 2 |
Is there a way I can output it like this using SQL SELECT?
| Item | ProdSched(1)(Qnty) | ProdSched(2)(Qnty) |
| a | 1 | 4 |
| b | 2 | 5 |
| c | 3 | 6 |
You can use PIVOT for this. If you have a known number of values to transform, then you can hard-code the values via a static pivot:
select item, [1] as ProdSched_1, [2] as ProdSched_2
from
(
select item, qty, prodsched
from yourtable
) x
pivot
(
max(qty)
for prodsched in ([1], [2])
) p
see SQL Fiddle with Demo
If the number of columns is unknown, then you can use a dynamic pivot:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(prodsched)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT item,' + #cols + ' from
(
select item, qty, prodsched
from yourtable
) x
pivot
(
max(qty)
for prodsched in (' + #cols + ')
) p '
execute(#query)
see SQL Fiddle with Demo
SELECT Item,
[ProdSched(1)(Qnty)] = MAX(CASE WHEN ProdSched = 1 THEN Qnty END),
[ProdSched(2)(Qnty)] = MAX(CASE WHEN ProdSched = 2 THEN Qnty END)
FROM dbo.tablename
GROUP BY Item
ORDER BY Item;
Let's hit this in two phases. First, although this is not the exact format you wanted, you can get the data you asked for as follows:
Select item, ProdSched, max(qty)
from Item1
group by item,ProdSched
Now, to get the data in the format you desired, one way of accomplishing it is a PIVOT table. You can cook up a pivot table in SQL Server as follows:
Select item, [1] as ProdSched1, [2] as ProdSched2
from ( Select Item, Qty, ProdSched
from item1 ) x
Pivot ( Max(qty) for ProdSched in ([1],[2])) y
Related
I want to swap the value of columns to have them sorted.
Table:
pid | category1 | category2 | category3
----+-----------+-----------+----------
1 | a | b |
2 | b | a |
3 | a | c | b
Result should be:
pid | category1 | category2 | category3
----+-----------+-----------+----------
1 | a | b |
2 | a | b |
3 | a | b | c
My approach was to select the columns to rows, order them in groups an return the new columns:
pid | category
----+---------
1 | a
1 | b
2 | b
2 | a
3 | a
3 | c
3 | b
I found the pivot function, but didn't really understand how to use it in this context.
Sean Lange is right about how you should correct your database schema.
Here is something to get you your results until then:
Using cross apply(values ...) to unpivot your data along with row_number() to reorder your category inside a common table expression; then repivoting that data with conditional aggregation:
;with cte as (
select
t.pid
, u.category
, rn = row_number() over (partition by t.pid order by u.category)
from t
cross apply (values (category1),(category2),(category3)) u(category)
where nullif(u.category,'') is not null
)
select
pid
, category1 = max(case when rn = 1 then category end)
, category2 = max(case when rn = 2 then category end)
, category3 = max(case when rn = 3 then category end)
from cte
group by pid
rextester demo: http://rextester.com/GIG22558
returns:
+-----+-----------+-----------+-----------+
| pid | category1 | category2 | category3 |
+-----+-----------+-----------+-----------+
| 1 | a | b | NULL |
| 2 | a | b | NULL |
| 3 | a | b | c |
+-----+-----------+-----------+-----------+
you can pivot as below
select * from (
select *, RowN = row_number() over(partition by pid order by Category) from Categories
) a
pivot (max(category) for RowN in ([1],[2],[3])) p
Output as below:
+-----+-----------+-----------+-----------+
| Pid | Category1 | Category2 | Category3 |
+-----+-----------+-----------+-----------+
| 1 | a | b | NULL |
| 2 | a | b | NULL |
| 3 | a | b | c |
+-----+-----------+-----------+-----------+
For dynamic list of columns you can use as below:
declare #cols1 varchar(max)
declare #cols2 varchar(max)
declare #query nvarchar(max)
--Row Numbers with tally
;with c1 as (
select * from ( values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) v(n) )
,c2 as (select n1.* from c1 n1, c1 n2, c1 n3, c1 n4)
,RowNumbers as (
select top (select max(cnt) from (select cnt = count(*) from Categories group by pid ) a) convert(varchar(6), row_number() over (order by (select null))) as RowN
from c2 n1, c2 n2
)
select #cols1 = stuff((select ','+QuoteName(RowN) from RowNumbers group by RowN for xml path('')),1,1,''),
#cols2 = stuff((select ',' + QuoteName(RowN) + ' as '+ QuoteName(concat('Category' , RowN)) from RowNumbers group by RowN for xml path('')),1,1,'')
select #cols1, #cols2
Set #query = ' Select Pid, '+ #cols2 +' from ( '
Set #query += ' select *, RowN = row_number() over(partition by pid order by Category) from Categories) a '
Set #query += ' pivot (max(category) for RowN in (' + #cols1 + ')) p'
--select #query
exec sp_executesql #query
Table1
| MODULE | COUNT | YEAR |
-------------------------
| M1 | 12 | 2011 |
| M1 | 43 | 2012 |
| M2 | 5 | 2011 |
| M3 | 24 | 2011 |
| M4 | 22 | 2011 |
| M4 | 11 | 2012 |
| M5 | 10 | 2012 |
I want to display like this
| MODULE | 2011 | 2012 |
----------------------------
| M1 | 12 | 43 |
| M2 | 5 | - |
| M3 | 24 | - |
| M4 | 22 | 11 |
| M5 | - | 10 |
This can be done using PIVOT query. Or the following:
select Module,
SUM(CASE WHEN Year='2011' then Count ELSE 0 END) as [2011],
SUM(CASE WHEN Year='2012' then Count ELSE 0 END) as [2012]
FROM T
GROUP BY Module
SQL Fiddle demo
You can use PIVOT for that:
SELECT
Module,
[2011], [2012]
FROM
(
SELECT
*
FROM Table1
) AS SourceTable
PIVOT
(
SUM([Count])
FOR [Year] IN ([2011], [2012])
) AS PivotTable;
You can also use this dynamic query if you don't have limited Year
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT distinct ',' + QUOTENAME([Year])
from Table1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = 'SELECT Module,' + #cols + '
FROM
(
Select *
FROM Table1
) dta
PIVOT
(
SUM([Count])
FOR [Year] IN (' + #cols + ')
) pvt '
EXECUTE(#query);
Result:
| MODULE | 2011 | 2012 |
----------------------------
| M1 | 12 | 43 |
| M2 | 5 | (null) |
| M3 | 24 | (null) |
| M4 | 22 | 11 |
| M5 | (null) | 10 |
See this SQLFiddle
Update
You can also use this alternative dynamic method: (Dynamic of the query given by #valex)
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT distinct ','
+ ' SUM(CASE WHEN YEAR= ''' + CAST(Year AS varchar(50))
+ ''' THEN [COUNT] ELSE ''-'' END) AS ' + QUOTENAME([Year])
from Table1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = 'SELECT Module, ' + #cols + '
FROM Table1 GROUP BY Module'
EXECUTE(#query);
See this SQLFiddle
SELECT *
FROM Tablename
PIVOT (
AVG([Count] FOR [Year] IN (2011, 2012, 2013)) AS AvgCount
)
I think you need to look into the relational operator PIVOT.
Using PIVOT and UNPIVOT
SELECT Module,
[2011], [2012]
FROM T
PIVOT (SUM(Count) FOR Year IN ([2011], [2012])) AS PivotTable;
I don't know if this would officially be called a pivot, but the result that I would like is this:
+------+---------+------+
| Alex | Charley | Liza |
+------+---------+------+
| 213 | 345 | 1 |
| 23 | 111 | 5 |
| 42 | 52 | 2 |
| 323 | | 5 |
| 23 | | 1 |
| 324 | | 5 |
+------+---------+------+
my input data is in this form:
+-----+---------+
| Apt | Name |
+-----+---------+
| 213 | Alex |
| 23 | Alex |
| 42 | Alex |
| 323 | Alex |
| 23 | Alex |
| 324 | Alex |
| 345 | Charley |
| 111 | Charley |
| 52 | Charley |
| 1 | Liza |
| 5 | Liza |
| 2 | Liza |
| 5 | Liza |
| 1 | Liza |
| 5 | Liza |
+-----+---------+
because I have approximately 100 names, I don't want to have to do a ton of sub queries lik this
select null, null, thirdcolumn from...
select null, seconcolumn from...
select firstcolumn from...
Is there a way to do this with PIVOT or otherwise?
You can do this with dynamic PIVOT and the ROW_NUMBER() function:
DECLARE #cols AS VARCHAR(1000),
#query AS VARCHAR(8000)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(Name)
FROM (SELECT DISTINCT Name
FROM #test
)sub
ORDER BY Name
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)')
,1,1,'')
PRINT #cols
SET #query = '
WITH cte AS (SELECT DISTINCT *
FROM #test)
,cte2 AS (SELECT *,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Apt)RowRank
FROM cte)
SELECT *
FROM cte2
PIVOT (max(Apt) for Name in ('+#cols+')) p
'
EXEC (#query)
SQL Fiddle - Distinct List, Specific Order
Edit: If you don't want the list to be distinct, eliminate the first cte above, and if you want to keep arbitrary ordering change the ORDER BY to (SELECT 1):
DECLARE #cols AS VARCHAR(1000),
#query AS VARCHAR(8000)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(Name)
FROM (SELECT DISTINCT Name
FROM #test
)sub
ORDER BY Name
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)')
,1,1,'')
PRINT #cols
SET #query = '
WITH cte AS (SELECT *,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY (SELECT 1))RowRank
FROM #test)
SELECT *
FROM cte
PIVOT (max(Apt) for Name in ('+#cols+')) p
'
EXEC (#query)
SQL Fiddle - Full List, Arbitrary Order
And finally, if you didn't want the RowRank field in your results, just re-use the #cols variable in your SELECT:
SET #query = '
WITH cte AS (SELECT *,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY (SELECT 1))RowRank
FROM #test)
SELECT '+#cols+'
FROM cte
PIVOT (max(Apt) for Name in ('+#cols+')) p
'
EXEC (#query)
Oh, this is something of a pain, but you can do it with SQL. You are trying to concatenate the columns.
select seqnum,
max(case when name = 'Alex' then apt end) as Alex,
max(case when name = 'Charley' then apt end) as Charley,
max(case when name = 'Liza' then apt end) as Liza
from (select t.*, row_number() over (partition by name order by (select NULL)) as seqnum
from t
) t
group by seqnum
order by seqnum;
As a note: there is no guarantee that the original ordering will be the same within each column. As you know, SQL tables are inherently unordered, so you would need a column to specify the ordering.
To handle multiple names, I'd just get the list using a query such as:
select distinct 'max(case when name = '''+name+''' then apt end) as '+name+','
from t;
And copy the results into the query.
I have a table like this . Table 1.
Customer Product Value Quantity Order Number
Dave Product 1 15 1 154
Dave Product 2 25 5 154
Dave Product 3 45 4 15
Rob Product 2 222 33 233
Now I want this , Table 2.
Customer Product 1 Quantity Product 1 Value Price per item ( Value /Quantity) for Product 1 Product 2 Quantity Product 2 Value Price per item ( Value /Quantity) for Product 2 Product 3 Quantity Product 3 Value Price per item ( Value /Quantity) for Product 3 Order Number
Dave 1 15 15 5 25 5 null null null 154
Dave null null null null null Null 4 45 11.25 15
Rob null null null 33 222 6.727272727 null null null 233
I was thinking about some pivot but wasn't sure how to construct it . Also the number of products is not fixed and change in Table 1.
In order to get the result, I would advise applying both an unpivot and a pivot to the data.
The UNPIVOT will convert the column data from your table into rows. Once the data is unpivoted, then you can apply a pivot.
Since you are using SQL Server 2008+ you can use CROSS APPLY with the VALUES clause to unpivot. Prior to 2008, you could use the UNPIVOT function. The code to unpivot the data is:
select t.customer,
replace(t.product, ' ', '')+'_'+c.col piv_col,
c.val,
t.ordernumber
from table1 t
cross apply
(
values
('value', cast(value as varchar(10))),
('quantity', cast(quantity as varchar(10))),
('PricePerUnit', cast((value/quantity) *1.0 as varchar(10)))
) c (col, val);
See Demo. This converts the data into the following format:
| CUSTOMER | PIV_COL | VAL | ORDERNUMBER |
---------------------------------------------------------
| Dave | Product1_value | 15 | 154 |
| Dave | Product1_quantity | 1 | 154 |
| Dave | Product1_PricePerUnit | 15.0 | 154 |
| Dave | Product2_value | 25 | 154 |
You can see that the row for Dave order 154 has been turned into rows and I have created the new column names that will be used for the pivot (piv_col). This column has concatenated the Product Name to the from of the previous column headers (value, quantity).
Since the data is in a single row, you can easily apply the pivot function to the data. The final code will be:
select customer,
Product1_quantity, Product1_value, Product1_PricePerUnit,
Product2_quantity, Product2_value, Product2_PricePerUnit,
Product3_quantity, Product3_value, Product3_PricePerUnit,
orderNumber
from
(
select t.customer,
replace(t.product, ' ', '')+'_'+c.col piv_col,
c.val,
t.ordernumber
from table1 t
cross apply
(
values
('value', cast(value as varchar(10))),
('quantity', cast(quantity as varchar(10))),
('PricePerUnit', cast((value/quantity) *1.0 as varchar(10)))
) c (col, val)
) d
pivot
(
max(val)
for piv_col in(Product1_quantity, Product1_value, Product1_PricePerUnit,
Product2_quantity, Product2_value, Product2_PricePerUnit,
Product3_quantity, Product3_value, Product3_PricePerUnit)
) piv;
See SQL Fiddle with Demo.
The above works great if you have a known number of products, but if not, then you will need to use dynamic SQL.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(replace(t.product, ' ', '')+'_'+c.col)
from Table1 t
cross apply
(
values ('value', 1), ('quantity', 0),('PricePerUnit', 3)
) c (col, so)
group by product, col, so
order by product, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT customer, ' + #cols + ', ordernumber
from
(
select t.customer,
replace(t.product, '' '', '''')+''_''+c.col piv_col,
c.val,
t.ordernumber
from table1 t
cross apply
(
values
(''value'', cast(value as varchar(10))),
(''quantity'', cast(quantity as varchar(10))),
(''PricePerUnit'', cast((value/quantity) *1.0 as varchar(10)))
) c (col, val)
) d
pivot
(
max(val)
for piv_col in (' + #cols + ')
) p '
execute(#query);
See SQL Fiddle with Demo. These queries give the result:
| CUSTOMER | PRODUCT1_QUANTITY | PRODUCT1_VALUE | PRODUCT1_PRICEPERUNIT | PRODUCT2_QUANTITY | PRODUCT2_VALUE | PRODUCT2_PRICEPERUNIT | PRODUCT3_QUANTITY | PRODUCT3_VALUE | PRODUCT3_PRICEPERUNIT | ORDERNUMBER |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Dave | (null) | (null) | (null) | (null) | (null) | (null) | 4 | 45 | 11.0 | 15 |
| Dave | 1 | 15 | 15.0 | 5 | 25 | 5.0 | (null) | (null) | (null) | 154 |
| Rob | (null) | (null) | (null) | 33 | 222 | 6.0 | (null) | (null) | (null) | 233 |
Hi I have two tables Attribute, Instance
Attribute Table
id | key_info | value_info
2 | amount | 1009
2 | currency | USD
2 | Level | Level 5
3 | amount | 2017
3 | currency | CAD
Instance Table
id | status
2 | Pending
3 | Approved
I want to join two tables like this-
New table
id | amount | currrency | level | status
2 | 1001 | USD | Level 5 | Pending
3 | 2017 | CAD | | Approved
All the fields in the Attribute and Instance are optional except id.
select
a.id,
max(case when a.key_info = 'amount' then a.value_info end) as amount,
max(case when a.key_info = 'currency' then a.value_info end) as currency,
max(case when a.key_info = 'level' then a.value_info end) as level,
i.status
from
attribute a
join instance i on a.id = i.id
group by
a.id,
i.status
Sql Fiddle
Starting in SQL Server 2005, the PIVOT function can perform this conversion from rows to columns:
select id,
amount,
currency,
level,
status
from
(
select i.id, i.status,
a.key_info,
a.value_info
from instance i
inner join attribute a
on i.id = a.id
) src
pivot
(
max(value_info)
for key_info in (amount, currency, level)
) piv
See SQL Fiddle with Demo.
If you have an unknown number of key_values then you can use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(key_info)
from Attribute
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id, ' + #cols + ', status from
(
select i.id, i.status,
a.key_info,
a.value_info
from instance i
inner join attribute a
on i.id = a.id
) x
pivot
(
max(value_info)
for key_info in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo. Both give the result:
| ID | AMOUNT | CURRENCY | LEVEL | STATUS |
-----------------------------------------------
| 2 | 1009 | USD | Level 5 | Pending |
| 3 | 2017 | CAD | (null) | Approved |