I'm working on a T-SQL issue where I needed to Transponse Rows into Columns and using UNPIVOT and PIVOT together per a post at Simple way to transpose columns and rows in Sql?
No problem. It does Exactly what I want with the row/column manipulation. However what I would REALLY like to do is to get the values used for the column headers to become yet another row in the results.
My abbreviated code is:
SELECT *
FROM (SELECT fiscalyear,
Sum(totalrecords) AS TotalRecords
FROM dbo.tbleirstatisticsoverviewsummary
WHERE fiscalquarter = 'ALL'
AND branchcode = 'ALL'
GROUP BY fiscalyear,
fiscalquarter,
branchcode) AS p
UNPIVOT (value
FOR colname IN ( totalrecords )) AS unpvt
PIVOT (Max(value) For FiscalYear IN ([2012],[2013],[ALL])) as p
What it renders is:
colname 2012 2013 ALL
TotalRecords 421 227 648
Where the first line is column headers.
Any thoughts on how I could get the column headers to be data rows?
Adding some sample Raw Data
fiscalyear TotalRecords
2012 421
2013 227
ALL 648
There are a few confusing things that you are doing.
First, typically you will unpivot multiple columns. Right now, you are are unpivoting one column and it seems like you are doing it just to rename the column?
Second, you are aggregating the data twice, the PIVOT should be able to handle the aggregation using SUM().
Third, it is not exactly clear on why you need the column headers as a row, what will you want the column headers to be called?
Based on your sample data you should be able to just apply the PIVOT function:
select 'TotalRecords' TotalRecords,
[2012],
[2013],
[All]
from tbleirstatisticsoverviewsummary
pivot
(
sum(totalrecords)
for FiscalYear IN ([2012],[2013],[ALL])
) p;
See SQL Fiddle with Demo. Then if you want a row with the columns headers, then you can use a UNION ALL:
select 'colname' col1,
2012 col2,
2013 col3,
'All' col4
union all
select 'TotalRecords' TotalRecords,
[2012],
[2013],
[All] = cast([all] as varchar(10))
from tbleirstatisticsoverviewsummary
pivot
(
sum(totalrecords)
for FiscalYear IN ([2012],[2013],[ALL])
) p;
See SQL Fiddle with Demo
Related
I have a table as bellow:
I want query to print output as bellow:
Note: Please, do not downvote. I know the rules of posting answers, but for such of questions there's no chance to post short answer. I posted it only to provide help for those who want to find out how to achieve that, but does not expect ready-to-use solution.
I'd suggest to read these articles:
PIVOT on two or more fields in SQL Server
Pivoting on multiple columns - SQL Server
Pivot two or more columns in SQL Server 2005
At first UNPIVOT then PIVOT. If number of rows for each Pod_ID is not always equal 3 then you need to use dynamic SQL. The basic sample:
SELECT *
FROM (
SELECT Pod_ID,
Purs + CASE WHEN RN-1 = 0 THEN '' ELSE CAST(RN-1 as nvarchar(10)) END as Purs,
[Values]
FROM (
SELECT Pod_ID,
Pur_Qty, --All columns that will be UNPIVOTed must be same datatype
Pur_Price,
CAST(ETD_Date as int) ETD_Date, -- that is why I cast date to int
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as RN
FROM YourTable
) as p1
UNPIVOT (
[Values] FOR [Purs] IN(Pur_Qty, Pur_Price, ETD_Date)
) as unpvt
) as p2
PIVOT (
MAX([Values]) FOR Purs IN (Pur_Qty,Pur_Price,ETD_Date,Pur_Qty1,Pur_Price1,ETD_Date1,Pur_Qty2,Pur_Price2,ETD_Date2)
) as pvt
Will bring you:
Pod_ID Pur_Qty Pur_Price ETD_Date Pur_Qty1 Pur_Price1 ETD_Date1 Pur_Qty2 Pur_Price2 ETD_Date2
F8E2F614-75BC-4E46-B7F8-18C7FC4E5397 24 22 20160820 400 33 20160905 50 44 20160830
I would like to add this expression:
([2013]/[2012]-1) AS [Change%]
As a field to the crosstab query below:
TRANSFORM
Sum(Data.Spending)
SELECT
Data.Category
FROM
Data
WHERE
(((Data.Year)="2012" Or (Data.Year)="2013"))
GROUP BY
Data.Category
PIVOT
Data.Year;
This is solved through using another table in this thread: Access 2007 Crosstab Query Expression
But for my purposes I need to have everything together in one query.
This is because I am writing SQL in another program, http://www.ljzsoft.com/ppt-report.htm, which uses the query to directly access an Access database.
As you have probably discovered, Access won't let us use a Crosstab query as a subquery. If we try to do
SELECT ...
FROM
(
TRANSFORM
we get "Syntax error in FROM clause". One workaround is to do all of the aggregations, create the derived (calculated) values, UNION them together, and then "crosstab" that. In your case:
TRANSFORM Sum(ColValue) AS SumOfColValue
SELECT Category
FROM
(
SELECT Category, [Year] AS ColHead, Sum(Spending) AS ColValue
FROM Data
WHERE [Year] IN ("2012", "2013")
GROUP BY Category, [Year]
UNION ALL
SELECT
curr.Category,
"Change%" AS ColHead,
(curr.SumOfSpending/prev.SumOfSpending-1) AS ColValue
FROM
(
SELECT Category, [Year], Sum(Spending) AS SumOfSpending
FROM Data
WHERE [Year] IN ("2012", "2013")
GROUP BY Category, [Year]
) AS curr
INNER JOIN
(
SELECT Category, [Year], Sum(Spending) AS SumOfSpending
FROM Data
WHERE [Year] IN ("2012", "2013")
GROUP BY Category, [Year]
) AS prev
ON prev.Category = curr.Category
AND CLng(prev.[Year]) = (CLng(curr.[Year]) - 1)
)
GROUP BY Category
PIVOT ColHead
which, for my sample data, returns
Category 2012 2013 Change%
--------- ---- ---- -----------------
Category1 123 345 1.80487804878049
Category2 234 456 0.948717948717949
If that is too nasty for your liking then you might want to investigate whether your reporting tool supports calculated report fields (e.g., in Crystal Reports I believe they are called "Formula Fields").
I have the following SQL:
SELECT
PhaseId,
COUNT(JoinId)
FROM Joins
GROUP BY
PhaseId
OUTPUT:
1 143
2 65
3 86
I usually pivot the result by using the case technique for each column, but now I'm trying to use the PIVOT statement unsuccessfully. Can anyone point me in the right direction?
I feel like there are many examples out there, but PIVOT is hard to wrap your head around, so:
SELECT *
FROM
( SELECT PhaseId,JoinID
FROM YourTable
) AS T1
PIVOT (COUNT(JoinID) FOR PhaseId IN ([1],[2],[3])) AS T2
I have a question which has been bugging me for a couple of days now. I have a table with:
Date
ID
Status_ID
Start_Time
End_Time
Status_Time(seconds) (How ling they were in a certain status, in seconds)
I want to put this data in another table, that has the Status_ID grouped up as columns. This table has columns like this:
Date
ID
Lunch (in seconds)
Break(in seconds)
Vacation, (in seconds) etc.
So, Status_ID 2 and 3 might be grouped under vacation, Status_ID 1 lunch, etc.
I have thought of doing a Case nested in a while loop, to go through every row to insert into my other table. However, I cannot wrap my head around inserting this data from Status_ID in rows, to columns that they are now grouped by.
There's no need for a WHILE loop.
SELECT
date,
id,
SUM(CASE WHEN status_id = 1 THEN status_time ELSE 0 END) AS lunch,
SUM(CASE WHEN status_id = 2 THEN status_time ELSE 0 END) AS break,
SUM(CASE WHEN status_id = 3 THEN status_time ELSE 0 END) AS vacation
FROM
My_Table
GROUP BY
date,
id
Also, keeping the status_time in the table is a mistake (unless it's a non-persistent, calculated column). You are effectively storing the same data in two places in the database, which is going to end up resulting in inconsistencies. The same goes for pushing this data into another table with times broken out by status type. Don't create a new table to hold the data, use the query to get the data when you need it.
This type of query (that transpose values from rows into columns) is named pivot query (SQL Server) or crosstab (Access).
There is two types of pivot queries (generally speaking):
With a fixed number of columns.
With a dynamic number of columns.
SQL Server support both types but:
Database Engine (query language: T-SQL) support directly only pivot
queries with a fixed number of columns(1) and indirectly (2)
Analysis Services (query language: MDX) support directly both types (1 & 2).
Also, you can query(MDX) Analysis Service data sources from T-SQL using OPENQUERY/OPENROWSET functions or using a linked server with four-part names.
T-SQL (only) solutions:
For the first type (1), starting with SQL Server 2005 you can use the PIVOT operator:
SELECT pvt.*
FROM
(
SELECT Date, Id, Status_ID, Status_Time
FROM Table
) src
PIVOT ( SUM(src.Status_Time) FOR src.Status_ID IN ([1], [2], [3]) ) pvt
or
SELECT pvt.Date, pvt.Id, pvt.[1] AS Lunch, pvt.[2] AS [Break], pvt.[3] Vacation
FROM
(
SELECT Date, Id, Status_ID, Status_Time
FROM Table
) src
PIVOT ( SUM(src.Status_Time) FOR src.Status_ID IN ([1], [2], [3]) ) pvt
For a dynamic number of columns (2), T-SQL offers only an indirect solution: dynamic queries. First, you must find all distinct values from Status_ID and the next move is to build the final query:
DECLARE #SQLStatement NVARCHAR(4000)
,#PivotValues NVARCHAR(4000);
SET #PivotValues = '';
SELECT #PivotValues = #PivotValues + ',' + QUOTENAME(src.Status_ID)
FROM
(
SELECT DISTINCT Status_ID
FROM Table
) src;
SET #PivotValues = SUBSTRING(#PivotValues,2,4000);
SELECT #SQLStatement =
'SELECT pvt.*
FROM
(
SELECT Date, Id, Status_ID, Status_Time
FROM Table
) src
PIVOT ( SUM(src.Status_Time) FOR src.Status_ID IN ('+#PivotValues+') ) pvt';
EXECUTE sp_executesql #SQLStatement;
I am looking for some help on designing a simple pivot so that I can link it into other parts of my queries.
My data is like this
Items Table
Below is my table if I run Select * from items
ITEM Weight
12345 10
12345 11
654321 50
654321 20
654321 100
There are hundreds of Items in this table but each item code will only ever have
maximum of 3 weight records each.
I want the desired output
ITEM Weight_1 Weight_2 Weight_3
12345 10 11 null
654321 50 20 100
Would appreciate any suggestions,
I have played around with pivots but each subsequent item puts the weights into weight 4,5,6,7,etc
instead of starting at weight1 for each item.
Thanks
Update
Below is what I have used so far,
SELECT r.*
FROM (SELECT 'weight' + CAST(Row_number() OVER (ORDER BY regtime ASC)AS
VARCHAR(10))
line,
id,
weight
FROM items it) AS o PIVOT(MIN([weight]) FOR line IN (weight1, weight2,
weight3)) AS r
You were almost there! You were only missing the PARTITION BY clause in OVER:
SELECT r.*
FROM (SELECT 'weight' + CAST(Row_number() OVER (PARTITION BY id ORDER BY
regtime ASC)
AS
VARCHAR(10)) line,
id,
weight
FROM items it) AS o PIVOT(MIN([weight]) FOR line IN (weight1, weight2,
weight3)) AS r
When you PARTITION BY by ID, the row numbers are reset for each different ID.
Update
You do not need dynamic pivot, since you will always have 3 weights. But, if you ever need dynamic number of columns, take a look at some of the examples here:
SQL Server PIVOT perhaps?
Pivot data in T-SQL
How do I build a summary by joining to a single table with SQL Server?
You will need a value to form the columns which I do with row_number. The outcome is what you want. The only negative that I have against PIVOT is that you need to know how many columns in advance. I use a similar method, but build up the select as dynamic SQL and can then insert my columns.
EDIT: updated to show columns as weight1, weight2, etc.
create table #temp (Item int, Weight int)
insert into #temp (Item, Weight)
Values (12345, 10),
(12345, 11),
(654321, 50),
(654321, 20),
(654321, 200)
SELECT *
FROM (SELECT Item,
Weight,
'weight' + cast(Row_number()
OVER (partition by Item order by item) as varchar(10)) as seq
FROM #temp) as Src
PIVOT ( MAX(Weight) FOR Seq IN ([Weight1], [Weight2], [Weight3]) ) as PVT
MySQL
Whenever you need a pivot, use group_concat it will output a CSV list of the values you need.
Once you get used to working with it, it's a great tool.
SELECT item, GROUP_CONCAT(weight) as weights FROM table1
GROUP BY item
See: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
TSQL aka SQL-server
Many many questions on this because T-SQL supports a pivot keyword.
See:
Transact SQL Query-Pivot-SQL
Pivot data in T-SQL