Select row to column SQL - sql

I didn't meet row to column before, can you help me?
This is example of my source.
And I want to select from:
Id Project ID Date Hour
-------------------------------
1 Project-1 1/1/2010 10
2 Project-1 1/2/2010 2
3 Project-1 1/3/2010 3
4 Project-1 1/4/2010 5
5 Project-2 1/1/2010 3
6 Project-2 1/2/2010 4
7 Project-2 1/3/2010 2
8 Project-2 1/4/2010 7
9 Project-3 1/1/2010 5
10 Project-3 1/2/2010 6
11 Project-3 1/3/2010 4
.
.
.
to
Project ID 1/1/2010 1/2/2010 1/3/2010 1/4/2010 ...
----------------------------------------------------------------
Project-1 10 2 3 5
Project-2 3 4 2 7
Project-3 5 6 4
Please help me.
UPDATE:
This is my solution.
--============== Create stored procedure ============
if exists(select * from sys.procedures where name='usp_ProjectFollow')
drop procedure usp_ProjectFollow
go
create proc usp_ProjectFollow
as
begin
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
Declare #ParamDefinition AS NVarchar(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME([Date])
FROM MyTable
group by [Date]
order by [Date]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') ,1,1,'')
set #query = 'SELECT id, ProjectName, ProjectCode, ' + #cols +
' FROM MyTable
pivot
(
sum(Hour)
for Date in (' + #cols + ')
) p '
--print #query
execute sp_Executesql #query
end
go
hope it's will help someone need.

A static solution is using this type of query:
SELECT
ProjectID,
SUM(CASE WHEN Date = '1/1/2010' THEN Hour ELSE 0 END) As '1/1/2010',
SUM(CASE WHEN Date = '1/2/2010' THEN Hour ELSE 0 END) As '1/2/2010',
SUM(CASE WHEN Date = '1/3/2010' THEN Hour ELSE 0 END) As '1/3/2010',
SUM(CASE WHEN Date = '1/4/2010' THEN Hour ELSE 0 END) As '1/4/2010',
...
FROM
yourTable
GROUP BY
ProjectID;
For using a dynamic solution you need to use dynamic SQL like this:
Declare #SQL nvarchar(MAX)
SELECT #SQL = ISNULL(#SQL, 'SELECT ProjectID') + ', SUM(CASE WHEN Date = ''' + [Date] + ''' THEN [Hour] ELSE 0 END) As [' + [Date] + ']'
FROM #t
GROUP BY [Date]
SELECT #SQL = #SQL + ' FROM yourTable GROUP BY ProjectID;'
EXEC(#SQL)

Related

how to convert Row to column dynamically in sql server

Here is my tables structure
Result
id ResultId CategoryId Total Attempted Score
----------------------------------------------------
1 8 1 30 25 20
2 8 2 30 30 19
3 8 3 30 27 21
4 7 1 20 15 10
5 7 2 20 20 15
Category
Id CategoryName
-----------------------
1 General
2 Aptitude
3 Technical
I want data in the below format
For ResultId = 8
Id General Aptitude Technical Total
--------------------------------------------------
8 20 19 21 60
For ResultId = 7
Id General Aptitude Total
-------------------------------------
7 10 15 25
I need a help to fetch the data in above format.
NOTE: The final fetched data contains score from Result table but having a column names from category table and according to CategoryId in Result table. Column name will be dynamic
Tried the below code (just for testing) but didn't work
DECLARE #SQLQuery AS NVARCHAR(MAX)
DECLARE #PivotColumns AS NVARCHAR(MAX)
SELECT #PivotColumns= COALESCE(#PivotColumns + ',','') + QUOTENAME(CategoryName)
FROM ( SELECT DISTINCT CategoryName
FROM [dbo].[Category] c) AS PivotExample
SET #SQLQuery =
N'SELECT DISTINCT ' + #PivotColumns + '
FROM [dbo].[Category] c
PIVOT( SUM(c.Id)
FOR CategoryName IN (' + #PivotColumns + ')) AS P'
EXEC sp_executesql #SQLQuery
My query will give you the expected output. You can check the output in SQL Fiddle
--dynamic with case
DECLARE #Sql NVARCHAR(4000) = NULL
DECLARE #ColumnHeaders NVARCHAR(4000);
SET #ColumnHeaders = STUFF((
SELECT DISTINCT ',' + 'Max(CASE WHEN rn =' + quotename(rn, '''') + ' THEN Score else null end ) as ' + CategoryName + CHAR(10) + CHAR(13)
FROM (
SELECT CategoryName, row_number() OVER (ORDER BY CategoryName) rn
FROM ( SELECT DISTINCT CategoryName FROM Category) t0
) t1
FOR XML PATH('')
,TYPE
).value('.', 'varchar(max)'), 1, 1, '');
--print #ColumnHeaders
SET #sql = N' ;with cte as (
select * , Row_number() Over(Partition by ResultId Order by ResultId ) rn from
Result)
Select ResultId, ' + #ColumnHeaders + ', SUM(Score) Total from cte Group by
ResultId ';
EXECUTE sp_executesql #sql

How to pivot a table this way when there are 3000 columns

I am using SQL Server
I have a table TT that looks like this
TargetID RowID Actual
0001 1 0
0001 2 1
0001 3 1
0002 1 0
0002 2 1
0002 3 0
0003 1 1
0003 2 1
0003 3 0
How can I pivot it is to this
RowID Target0001 Target0002 Target0003
1 0 0 1
2 1 1 1
3 1 0 0
I tried
SELECT 'TargetID' + TargetID, RowID, Actual
FROM TT
WHERE TargetID = '0001'
UNION ALL
SELECT 'TargetID' + TargetID, RowID, Actual
FROM TT
WHERE TargetID = '0002'
SELECT 'TargetID' + TargetID, RowID, Actual
FROM TT
WHERE TargetID = '0003'
But there are 3000 TargetIDs and my method is not good for that
Any idea how to do that?
You can try this...
DECLARE #ColumnsTable TABLE ([ColumnName] VARCHAR(50));
INSERT INTO #ColumnsTable ([ColumnName])
SELECT DISTINCT '[' + CONVERT(VARCHAR(48), [TargetID]) + ']'
FROM TT;
DECLARE #PivotColumns VARCHAR(MAX), #TotalColumn VARCHAR(MAX), #SQL VARCHAR(MAX);
SET #PivotColumns = (SELECT STUFF((SELECT DISTINCT ', ' + CONVERT(VARCHAR(50), [ColumnName])
FROM #ColumnsTable
FOR XML PATH('')), 1, 2, ''));
SET #SQL = 'SELECT RowID,' +#PivotColumns +'
FROM (
SELECT RowID,TargetID,Actual
FROM TT) AS t
PIVOT (MAX([Actual])
FOR [TargetID] IN (' + #PivotColumns + ')) AS p';
EXEC(#SQL);

SQL Server 2016 Pivot

I have one question regarding sql (MS SQL 2016) and pivot functionality.
First let me explain about the data structure.
Examples of tbl_Preise. There are several prices (Preis) for each area (Gebiet_von, Gebiet_bis) in relays (StaffelNr). All connected to the same freight (Fracht_id). There can be a different number of relays for each freight. All of these relays repeat for each area, so i.e. there is one price for relay 1 in area 1800 - 1899, but there is another price for relay 1 for area 1900 - 1999.
This is how the table tbl_Preise looks:
autoID Fracht_id Gebiet_von Gebiet_bis Zielland_Nr StaffelNr Preis Mindestpreis Mautkosten
16933 4 1800 1899 4 1 22,6481 0,00 0,00
16934 4 1800 1899 4 2 37,0843 0,00 0,00
16935 4 1800 1899 4 3 54,9713 0,00 0,00
16936 4 1900 1999 4 1 23,4062 0,00 0,00
16937 4 1900 1999 4 2 84,4444 0,00 0,00
Now I have another table tbl_Fracht_Staffeln where the quantity of the relay is saved.
This table looks like:
id fracht_id staffelNr menge
18 4 1 50
19 4 2 100
20 4 3 150
21 4 4 200
Now I want to combine these data, which can vary through different number of relays to each freight.
I have done this via this query:
DECLARE #cols AS NVARCHAR(MAX),#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(staffelNr)
from tbl_Preise (nolock)
where fracht_id = #freightId
group by staffelNr
order by StaffelNr
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'
SELECT
Bezeichnung,
fracht_id,
gebiet_von,
gebiet_bis,
' + #cols + N'
from
(
select
l.Bezeichnung as Bezeichnung,
Zielland_Nr,
tbl_Preise.fracht_id,
gebiet_von,
gebiet_bis,
preis,
tbl_Preise.staffelNr as staffelNr
from
tbl_Preise (nolock)
left join
[dbo].[vw_Laender] l on tbl_Preise.Zielland_Nr = l.[Nummer]
where
tbl_Preise.Fracht_id = ' + cast(#freightId as nvarchar(100)) + '
) x
pivot
(
max(preis)
for staffelNr in (' + #cols + N')
) p
order by
gebiet_von, gebiet_bis'
exec sp_executesql #query;
This query gives me this result:
Bezeichnung fracht_id gebiet_von gebiet_bis 1 2 3 4 5 6
Germany 4 01800 01899 NULL NULL NULL NULL NULL NULL
Germany 4 06400 06499 NULL NULL NULL NULL NULL NULL
Germany 4 1800 1899 22,6481 37,0843 54,9713 64,4062 84,4444 94,6546
Germany 4 20500 20599 17,9088 27,3983 40,8845 46,7485 61,4905 67,835
Germany 4 21200 21299 17,9088 27,3983 40,8845 46,7485 61,4905 67,835
Germany 4 21500 21599 17,9088 27,3983 40,8845 46,7485 61,4905 67,835
Don't look exactly on the prices and the area codes. I've changed some in my example of tbl_Preise to make the relation and sense more clear.
So far so good. But now, as you can see, I have the staffelNr (1,2,3,4,...) as Header in my table.
I need there the column menge of table tbl_Fracht_Staffeln instead.
I tried already some joins and other stuff, but all did not work, because I have found no way to connect the column names (1,2,3,4...) to the table tbl_Fracht_Staffeln. Is there any way to achieve this?
Thank you very much in advance for help!
To do this you need to play with column header 2 times -
DECLARE #cols AS NVARCHAR(MAX),#query AS NVARCHAR(MAX) , #freightId as int , #cols1 AS NVARCHAR(MAX)
select #freightId = 4
select #cols = STUFF((SELECT ',' + QUOTENAME(t1.staffelNr) + ' as ' + QUOTENAME(t2.menge )
from tbl_Preise t1 (nolock)
join tbl_Fracht_Staffeln t2(nolock)
on t1.fracht_id = t2.fracht_id and t1.staffelNr = t2.staffelNr
where t1.fracht_id = #freightId
group by t1.staffelNr , t2.menge
order by t1.StaffelNr
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #cols1 = STUFF((SELECT ',' + QUOTENAME(staffelNr)
from tbl_Preise (nolock)
where fracht_id = #freightId
group by staffelNr
order by StaffelNr
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'
SELECT
fracht_id,
gebiet_von,
gebiet_bis,
' + #cols + N'
from
(
select
Zielland_Nr,
tbl_Preise.fracht_id,
gebiet_von,
gebiet_bis,
preis,
tbl_Preise.staffelNr as staffelNr
from
tbl_Preise (nolock)
where
tbl_Preise.Fracht_id = ' + cast(#freightId as nvarchar(100)) + '
) x
pivot
(
max(preis)
for staffelNr in (' + #cols1 + N')
) p
order by
gebiet_von, gebiet_bis'
print #query
exec sp_executesql #query;

Get last 2 years value and oldest year SQL

I have the following table:
--------------------------------------------------------------
InspectYear Part Pos1 Pos2 Pos3 Pos4
--------------------------------------------------------------
2009 001 8 8 9 7
2009 002 9 7 8 6
2011 001 9 9 8 7
2011 002 7 8 6 8
2013 001 8 9 7 9
2013 002 7 7 8 8
2015 001 8 8 7 4
2015 002 7 6 9 8
The InspectYeardata will always add every 2 years for each Part.
I want to calculate the newest value on each pos# column with the previous year (Calc1). Also the newest value with the oldest value (Calc2). I want to have the following result:
---------------------------------------------------------------------
Part Pos 2009 2011 2013 2015 Calc1 Calc2
---------------------------------------------------------------------
001 Pos1 8 9 8 8 0 0
001 Pos2 8 9 9 8 -1 0
001 Pos3 9 8 7 7 0 -2
001 Pos4 7 7 9 4 -5 -3
For the pivot thing, I did the following code but don't know for the calculation:
declare #inspectyear as nvarchar(max), #query as nvarchar(max);
set #inspectyear = STUFF((select distinct ',' + quotename(InspectYear) from #t2 c
for XML path(''), type).value('.','NVARCHAR(MAX)'),1,1,'')
set #query =
';with data as
(
select inspectyear,
partno, Pos, number
from #t2
unpivot
(
number
for Pos in ([Pos1], [Pos2], [Pos3], [Pos4])
) unpvt
)
select * into ##temp
from data
pivot
(
sum(number)
for inspectyear in (' + #inspectyear + ')
) pvt
order by Part'
exec sp_executesql #query = #query;
select * from ##temp;
drop table ##temp;
From the table above, Calc1 is value on 2015 minus value on 2013.Calc2 is value on 2015 minus value on 2009.
The formula above will change when new records created on 2017. So the formula for Calc1 is always get the last 2 values on years. Calc2 will always get the newest value minus the oldest value.
Does anyone have an idea for this ?
Thank you.
You want to add in these 2 calculated columns as you insert into ##temp, but you need to specify what they are at the same time as you build your dynamic query. So you use the same method as you did to get the column names - you build a string.
At the top of your workings, add in another string variable, #calc:
declare #inspectyear as nvarchar(max), #calc as nvarchar(max), #query as nvarchar(max);
set #inspectyear = STUFF((select distinct ',' + quotename(InspectYear) from ##t2 c
for XML path(''), type).value('.','NVARCHAR(MAX)'),1,1,'')
select #calc = ', ' + quotename(Max(InspectYear)) + ' - ' + quotename(Max(InspectYear)-2)
+ ' as Calc1, ' + quotename(Max(InspectYear)) + ' - ' + quotename(min(InspectYear))
+ ' as Calc2' from #t2;
Then include that in your dynamic query string as follows:
set #query =
';with data as
(
select inspectyear,
partno, Pos, number
from #t2
unpivot
(
number
for Pos in ([Pos1], [Pos2], [Pos3], [Pos4])
) unpvt
)
select * ' + #calc + ' into ##temp
from data
pivot
(
sum(number)
for inspectyear in (' + #inspectyear + ')
) pvt
order by partno';
exec(#query);
This should add the extra columns to ##temp as required.

SQL Multiple dynamic queries to one query

I am working in a SQL Server 2008 environment with SQL Server Management Studio 2012.
I have written 3 separate queries
QUERY 1
SQL query SUMS all the STOCK ON HAND from an Inventories table
SELECT StockCode,
Sum(QtyOnHand) AS 'SOH'
FROM InvWarehouse
WHERE StockCode NOT LIKE '%DEM%' AND StockCode NOT LIKE '%REF%' AND StockCode NOT LIKE 'Z%'
GROUP BY InvWarehouse.StockCode
QUERY 2
This query looks at future orders from a Purchase Orders Table and dynamically returns the next/following 12 months
DECLARE
#cols AS NVARCHAR(MAX),
#cols1 AS NVARCHAR(MAX),
#cols2 AS NVARCHAR(MAX),
#cols3 AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(YearMonth)
FROM
-- Selecting Using the Destinct --
(SELECT DISTINCT CAST(YEAR([OrderDueDate]) AS NVARCHAR(4)) + RIGHT('00' + CAST(MONTH([OrderDueDate]) AS NVARCHAR(2)),2) AS YearMonth
FROM PorMasterHdr
JOIN PorMasterDetail
ON PorMasterDetail.PurchaseOrder = PorMasterHdr.PurchaseOrder
WHERE DATEDIFF(MONTH, OrderDueDate, DATEADD(m,12,GETDATE())) <= 12 ) sub
ORDER BY YearMonth
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,''),
#cols2 = STUFF((SELECT ',ISNULL(' + QUOTENAME(YearMonth) + ',0) AS ' + QUOTENAME(YearMonth)
FROM
-- Selecting Using the Destinct --
(SELECT DISTINCT CAST(YEAR([OrderDueDate]) AS NVARCHAR(4)) + RIGHT('00' + CAST(MONTH([OrderDueDate]) AS NVARCHAR(2)),2) AS YearMonth
FROM PorMasterHdr
JOIN PorMasterDetail
ON PorMasterDetail.PurchaseOrder = PorMasterHdr.PurchaseOrder
WHERE DATEDIFF(MONTH, OrderDueDate, DATEADD(m,12,GETDATE())) <= 12) sub
ORDER BY YearMonth
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #query = '
SELECT MStockCode, ' + #cols2 + '
FROM (
SELECT MStockCode,
MOrderQty,
CAST(YEAR([OrderDueDate]) AS NVARCHAR(4))+RIGHT(''00''+CAST(MONTH([OrderDueDate]) AS NVARCHAR(2)),2) AS YearMonth
FROM PorMasterHdr
JOIN PorMasterDetail
ON PorMasterDetail.PurchaseOrder = PorMasterHdr.PurchaseOrder
WHERE MStockCode NOT LIKE ''%DEM%'' AND MStockCode NOT LIKE ''%REF%'' AND MStockCode NOT LIKE ''Z%''
) AS X
PIVOT (
SUM(MOrderQty)
FOR YearMonth in (' + #cols + ')
) AS PT'
EXECUTE (#query)
QUERY 3
This query looks at the past 12 month of sales data from a Sales table and dynamically returns the last/previous 12 months
DECLARE
#cols AS NVARCHAR(MAX),
#cols1 AS NVARCHAR(MAX),
#cols2 AS NVARCHAR(MAX),
#cols3 AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(YearMonth)
FROM
-- Selecting Using the Destinct --
(SELECT DISTINCT CAST([TrnYear] AS NVARCHAR(4)) + RIGHT('00' + CAST([TrnMonth] AS NVARCHAR(2)),2) AS YearMonth
FROM ArTrnDetail
WHERE DATEDIFF(MONTH, InvoiceDate, GETDATE()) <= 12 ) sub
ORDER BY YearMonth
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,''),
#cols2 = STUFF((SELECT ',ISNULL(' + QUOTENAME(YearMonth) + ',0) AS ' + QUOTENAME(YearMonth)
FROM
-- Selecting Using the Destinct --
(SELECT DISTINCT CAST([TrnYear] AS NVARCHAR(4)) + RIGHT('00' + CAST([TrnMonth] AS NVARCHAR(2)),2) AS YearMonth
FROM ArTrnDetail
WHERE DATEDIFF(MONTH, InvoiceDate, GETDATE()) <= 12) sub
ORDER BY YearMonth
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #query = '
SELECT StockCode, ' + #cols2 + '
FROM (
SELECT StockCode,
QtyInvoiced,
CAST([TrnYear] AS NVARCHAR(4))+RIGHT(''00''+CAST([TrnMonth] AS NVARCHAR(2)),2) AS YearMonth
FROM ArTrnDetail
WHERE StockCode NOT LIKE ''%DEM%'' AND StockCode NOT LIKE ''%REF%'' AND StockCode NOT LIKE ''Z%''
) AS X
PIVOT (
SUM(QtyInvoiced)
FOR YearMonth in (' + #cols + ')
) AS PT'
EXECUTE (#query)
The results for each query are correct. Now how do I combine them into one query. So that they return
STOCKCODE | Past 12 Month Sales Per Month | Stock On Hand | Future Purchases
Helicopters | 1 4 5 2 3 4 6 1 3 2 3 2| 15 | 2 3 5 4 6 7 8 4 3 2 8 5
Jam | 2 5 6 4 8 5 8 5 7 2 1 2| 30 | 4 5 6 5 8 7 0 1 2 1 1 4
Frogs | 2 3 2 4 8 5 4 6 8 2 1 3| 7 | 5 7 8 8 6 7 4 0 1 2 1 2
STOCK CODE for the above is the same information from the different tables eg. Helicopters in Inventory is the same as Helicopters in Purchase Orders.
I would suggest the following:
Rewrite #query2 to result in two columns: StockCode and Sales. Instead of selecting each month as a seperate column, concatenate each month in a VARCHAR. You already wrote a variable for #cols for selecting the columns seperately. Keep that for pivoting. Write a variable (#SelSales) to concatenate the results for each month in a VARCHAR and use that in your selection for the Sales column.
Rewrite #query3 to result in two columns: StockCode and Purchases (similar to 1.)
Put your #query1 in a NVARCHAR(MAX) variable (the one selecting the stock).
Write a #query to combine them all.
TSQL outline for #query:
DECLARE #query NVARCHAR(MAX);
SET #query=N'
SELECT
COALESCE(stock.StockCode,sales.StockCode,purchases.StockCode) AS StockCode,
COALESCE(sales.Sales,''0 0 0 0 0 0 0 0 0 0 0 0'') AS Sales,
COALESCE(stock.SOH,0) AS Stock,
COALESCE(purchases.Purchases,''0 0 0 0 0 0 0 0 0 0 0 0'') AS Purchases
FROM
('+#query1+') AS stock
FULL JOIN ('+#query2+') AS sales ON sales.StockCode=stock.StockCode
FULL JOIN ('+#query3+') AS purchases ON purchases.StockCode=stock.StockCode';
EXEC(#query);