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

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

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

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

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

SQL Server : Transpose rows to columns

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);

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