SQL Server : Transpose rows to columns - sql

I am trying to do transpose data. The number of columns are not fixed(i.e. selected ShiftNames are not fixed). Here is my input data.
Date_time ShiftName Consumption
28-07-2016 Shift 1 20
28-07-2016 Shift 2 21
28-07-2016 Shift 3 22
29-07-2016 Shift 1 30
29-07-2016 Shift 2 31
29-07-2016 Shift 3 32
30-07-2016 Shift 1 40
30-07-2016 Shift 2 41
30-07-2016 Shift 3 42
And the output will be like this
Shift 1 Shift 2 Shift 3 Date_Time
20 21 23 28-07-2016
30 31 32 29-07-2016
40 41 42 30-07-2016

You can do this with an pivot. Here is an example:
Test data:
DECLARE #temp TABLE(Date_time varchar(100), ShiftName VARCHAR(100), Consumption INT)
INSERT INTO #temp
VALUES
('28-07-2016','Shift 1',20),
('28-07-2016','Shift 2',21),
('28-07-2016','Shift 3',22),
('29-07-2016','Shift 1',30),
('29-07-2016','Shift 2',31),
('29-07-2016','Shift 3',32),
('30-07-2016','Shift 1',40),
('30-07-2016','Shift 2',41),
('30-07-2016','Shift 3',42)
Pivot:
SELECT
*
FROM
(
SELECT
Date_time,
ShiftName,
Consumption
FROM
#temp
) AS sourceTable
PIVOT
(
SUM(Consumption)
FOR ShiftName IN ([Shift 1],[Shift 2],[Shift 3])
) AS pvt
Result:
Date_time Shift 1 Shift 2 Shift 3
28-07-2016 20 21 22
29-07-2016 30 31 32
30-07-2016 40 41 42
Reference:
Using PIVOT and UNPIVOT

Since the shiftName is dynamic,use this dynamic query
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SELECT
#cols = STUFF((SELECT distinct ',' + QUOTENAME(ShiftName )
FROM
#temp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #query = 'SELECT * FROM
(
SELECT * FROM #temp
) x
PIVOT
(
Sum(consumption)
FOR ShiftName IN (' + #cols + ')
) p '
EXECUTE(#query);

Related

Sum of Current + Previous X Nth Rows

I am looking to find the sum of the current + the last X Nth Rows. I am able to do this with the following query, however it is not very scalable.
SELECT [id], [amount] + LAG([amount],6) OVER(ORDER BY [id]) + LAG([amount],12) OVER(ORDER BY [id]) + LAG([amount],18) OVER(ORDER BY [id])
If this example, I'm finding the current value of "amount", plus the last 3 "amounts" split 6 apart:
X = 3
N = 6
I will be using these within dynamic queries and would prefer not to build such a complex query each time. There could be many "lags" in some of the queries. Is there another way to write this query that would be more scalable?
SOURCE DATA
ID
Amount
1
107.35
2
105.41
3
104.63
4
106.7
5
108.7
6
110.21
7
108.8
8
108.91
9
108.5
10
106.66
11
105.2
12
106.5
13
108.27
14
109.72
15
111.53
16
112.8
17
109.03
18
115.31
19
115.56
20
116.85
21
116.08
22
117.61
23
118.31
24
119.25
25
118.45
26
118.43
27
120.16
28
122.5
29
125.57
30
125.65
EXPECTED RESULTS
ID
SUM OF LAST 4
1
NULL
2
NULL
3
NULL
4
NULL
5
NULL
6
NULL
7
NULL
8
NULL
9
NULL
10
NULL
11
NULL
12
NULL
13
NULL
14
NULL
15
NULL
16
NULL
17
NULL
18
NULL
19
439.98
20
440.89
21
440.74
22
443.77
23
441.24
24
451.27
25
451.08
26
453.91
27
456.27
28
459.57
29
458.11
30
466.71
At a best guess, it seems like what you want is something like this:
DECLARE #X int = 3,
#N int = 6;
SELECT YT.ID,
YT.Amount,
CASE WHEN ROW_NUMBER() OVER (PARTITION BY G.Grp ORDER BY ID) < #X+1 THEN NULL
ELSE SUM(Amount) OVER (PARTITION BY G.Grp ORDER BY ID
ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)
END
FROM dbo.YourTable YT
CROSS APPLY (VALUES(ID % #N))G(Grp)
ORDER BY YT.ID;
You'll note, however, that the 3 is hardcoded in one place, as you cannot use a variable for the ROWS BETWEEN clause. If you need to parametrise this, you'll need to use dynamic SQL:
DECLARE #X int = 3,
#N int = 6;
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = CONCAT(N'SELECT YT.ID,', #CRLF,
N' YT.Amount,', #CRLF,
N' CASE WHEN ROW_NUMBER() OVER (PARTITION BY G.Grp ORDER BY ID) < #X+1 THEN NULL', #CRLF,
N' ELSE SUM(Amount) OVER (PARTITION BY G.Grp ORDER BY ID', #CRLF,
N' ROWS BETWEEN ',#X,N' PRECEDING AND CURRENT ROW)', #CRLF, --I don't like injecting raw values, but if #X is an int, it is "safe"
N' END', #CRLF,
N'FROM dbo.YourTable YT', #CRLF,
N' CROSS APPLY (VALUES(ID % #N))G(Grp)', #CRLF,
N'ORDER BY YT.ID;');
PRINT #SQL; --Your best debugging friend
EXEC sys.sp_executesql #SQL, N'#X int, #N int', #X, #N;
db<>fiddle

Create different tables from 1 source table using Dynamic SQL

I have a sourcetable, from which I want to create different tables, using dynamic SQL.
Sourcetable:
ACCP_AcceptantID TransactionID Descrption Period AcceptantNumber
1 12 A 201801 16
1 13 AA 201801 16
2 21 B 201801 22
2 46 BB 201801 22
3 31 C 201801 54
3 38 CC 201801 54
4 94 D 201801 62
4 96 DD 201801 62
To be able to use a WHILE Loop to select each acceptant, I created a Table Acceptants:
SELECT DISTINCT ACCP_AcceptantNr
INTO Acceptants
FROM Sourcetable
ALTER TABLE #TussentabelAcceptanten ADD ID INT IDENTITY
ACCP_AcceptantNr ID
16 1
22 2
54 3
62 4
Desired Result (for each acceptantID, own table with content):
ACCP_AcceptantID TransactionID Descrption Period AcceptantNumber
1 12 A 201801 16
1 13 AA 201801 16
My query attempt:
DECLARE #SQL NVARCHAR(MAX),
#Acceptant NVARCHAR(40),
#Row INT = 1
WHILE #Row <= ( SELECT MAX(ID) FROM Acceptants)
BEGIN
SELECT #Acceptant = ACCP_AcceptantNr FROM Acceptants
SET #SQL = 'SELECT *
INTO MyDatabase.dbo.'+ Period + #Acceptant'
FROM #SourceTable
WHERE ACCP_AcceptantNr = '+ #Acceptant''
EXEC (#SQL)
SET #Row = #Row + 1
SET #SQL = ''
END
Error message: Incorrect syntax near '
Dynamic SQL always gets the better of me. I just don't get where to place the quotes. Suggestions would be great. Thanks.
You are missing the + symbols
SET #SQL = 'SELECT *
INTO MyDatabase.dbo.'+ Period + #Acceptant+'--Added +
FROM #SourceTable
WHERE ACCP_AcceptantNr = '+ #Acceptant -- Removed ''
Since you are Giving Period without an # symbol, I assume it is a column from some table, so add a selet from that table otherwise this will throw an error.
Like this
SELECT #SQL = 'SELECT *
INTO MyDatabase.dbo.'+ Period + #Acceptant+'--Added +
FROM #SourceTable
WHERE ACCP_AcceptantNr = '+ #Acceptant -- Removed ''
FROM YourTableName

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.

PIVOT in SQL Server 2012 - new to SQL [duplicate]

This question already has answers here:
Efficiently convert rows to columns in sql server
(5 answers)
Closed 6 years ago.
I am having some trouble with the next situation: I need to select 4 position for a statement and I need this positions to be pivoted as columns. The database is structured as: StatementId 1, PositionId 2, RepCurrValue 3. For example for StatementId = 55 and for the positions = 58 OR 62 OR 67 OR 82 the result is:
1 2 3
-----------------
55 58 146,8000
55 62 59,9800
55 67 800,0500
55 82 136,7600
and I want it to be
1 58 62 67 82
---------------------------------------
55 146,8000 59,9800 800,0500 136,7600
Thank you very much for your support.
I did this exercise with PIVOT table that could be help you, considering the column headings equal to the contents of the field [2] and NOT providing repeated values ​​in column [2]:
DECLARE #cols AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT ',[' + convert(nvarchar,(t.[2])) + ']' AS ID
FROM TB_1 t
FOR XML PATH(''), TYPE).value('.', 'varchar(max)'),1,1, '')
DECLARE #query AS NVARCHAR(MAX);
SET #query = N'SELECT p.[1],' + #cols + N' from
(
SELECT [1],[2],[3] FROM TB_1
) x
pivot
(
max([3])
for [2] in (' + #cols + N')
) p
'
exec sp_executesql #query;
below the result obtained by adding an additional StatementId (56) and new different positions (90,91) associated with StatementId (56)
1 58 62 67 82 90 91
55 146,8000 59,9800 800,0500 136,7600 NULL NULL
56 NULL NULL NULL NULL 185,74 185,74
a non-dynamic solution can be:
select *
from
(
SELECT [1],[2],[3] FROM TB_1
) x
pivot
(
max([3])
for [2] in ([58] , [62] , [67] , [82] )
) p

How to split group result into unlimited individual columns

If I have a table:
id tstamp x y
-----------------
1 111 1 A
2 111 2 B
3 111 3 C
4 222 1 D
5 222 2 E
6 222 3 F
7 333 1 G
8 333 2 H
9 333 3 I
... nnn ... ...
How can I split it into the following, where the number of columns is dependent on a range of tstamp?
x y111 y222 y333 ynnn
----------------------------------
1 A D G ...
2 B E H ...
3 C F I ...
It seems fundamentally simple, but I can't find any relevant example or API? There's a good 10 or so similar questions but they all deal with string handling, csv, xml???
I imagine it would be pretty straight forward to do in c# or another scripting language, but I would expect it should be an easy thing in sql?
you need to construct column values of tstamp for PIVOT and use dynamic query
DECLARE #cols NVARCHAR(MAX)
SELECT #cols = STUFF(( SELECT DISTINCT TOP 100 PERCENT
'],[' + tstamp
FROM Table1
ORDER BY '],[' + tstamp
FOR XML PATH('')
), 1, 2, '') + ']'
DECLARE #query NVARCHAR(4000)
SET #query = N'SELECT x, '+
#cols +'
FROM
(SELECT x, y, tstamp
FROM Table1
) p
PIVOT
(
MAX(y)
FOR tstamp IN
( '+
#cols +' )
) AS pvt
'
exec sp_executesql #query