Convert rows from a NoSQL schema to columns in SQL Server - sql-server

I understand how to use PIVOT to rotate rows into columns but I have a unique scenario where rows also have to be grouped. The source data is from a NoSQL database schema (un-relational). Here is an example of the source data:
ID case_id field_id sequence_number textvalue
1 12897 25 100 AAAAA
2 12897 50 100 BBBBB
3 12897 75 100 CCCCC
4 13587 25 200 DDDDD
5 13587 50 200 EEEEE
6 13587 75 200 FFFFF
7 13587 100 200 GGGGG
The result I need is:
case_id value_field_id_25 value_field_id_50 value_field_id_75 value_field_id_100
12897 AAAAA BBBBB CCCCC
13587 DDDDD EEEEE FFFFF GGGGG
So, what I need is a row of related records grouped by sequence_number. The number of rows with the same sequence_number is dynamic (it varies).
Any ideas?

Declare #SQL varchar(max) = Stuff((Select ',' + QuoteName('value_field_id_'+cast(field_id as varchar(25)))
From (Select Distinct Top 100 Percent field_id
From YourTable
Order by 1) A
For XML Path('')),1,1,'')
Select #SQL = '
Select [case_id],' + #SQL + '
From (
Select sequence_number
,case_id
,ColName = ''value_field_id_''+cast(field_id as varchar(25))
,Value = textvalue
From YourTable A
) A
Pivot (max([Value]) For [ColName] in (' + #SQL + ') ) p'
Exec(#SQL);
Returns

Related

T-SQL Help require for Pivoting multiple columns

I have a table
City Region Zone Passcode SureveyBy SureveyDone SureveyExpiry
Noida Sector 62 East 1 ABC Y NULL
Noida Sector 2 West 1 XYZ N 6/10/2016
Delhi CP Cnt 10 ABC N 10/10/2018
Delhi KB West 11 RST Y NULL
I need result in this format
Final Result
DECLARE #col NVARCHAR(max),#sql NVARCHAR(max)
SELECT #col=ISNULL(#col+',[','[')+t.SureveyBy+']' FROM dbo.myTable AS t GROUP BY t.SureveyBy
PRINT #col
SET #sql='
SELECT * FROM dbo.myTable
PIVOT(MAX(SureveyDone) FOR SureveyBy IN ('+#col+')) p'
EXEC(#sql)

SQL Server Pivot Multiple Values

Using SQL Server 2012.
I have the following table. The Style and colour are passed as a parameter:
Style Colour Size Whse Stock Sales 4WeekSales ATP
ABC123 AS12 10 London 2 3 6 7
ABC123 AS12 12 London 4 6 8 10
ABC123 AS12 14 New York 6 8 9 12
ABC123 AS12 10 New York 7 5 7 5
But need the data to look like this with the sizes along the top:
Whse 10 12 14
Lon
Stock 2 4 6
Sales 3 6 8
4WeekSales 6 8 9
ATP 7 10 12
New York
Stock 7 6
Sales 5 8
4WeekSales 7 9
ATP 5 12
Points to note - the size field needs to be dynamic - sometimes it can be 6 /8/10/12, sometimes it can be XS/S/M/L etc
Also their are more than two whse's - this is just an example.
I did make a start in T-SQL:
use SafetyStock
go
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', p.' + QUOTENAME(Size)
FROM (SELECT p.Size FROM dbo.vw_optimums AS p
GROUP BY p.Size) AS x;
SET #sql = N'
SELECT SKU, Style,' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT SKU, Style, p.Size, p.SAFETYSTOCK
FROM dbo.vw_optimums AS p
) AS j
PIVOT
(
SUM(SAFETYSTOCK) FOR Size IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
) AS p;';
PRINT #sql;
EXEC sp_executesql #sql;
However, this works but only pivots on the stock - how do I also pivot by Sales\4WeekSales\ATP and also groupb by the whse?
Thank you in advance.
This is my latest code. If I take the SEQNO out it works, but I need this so the sizes appear along the top correctly e.g. S / M / L / XL / XXL etc or 6/ 8 / 10 / 12
DECLARE #SizeColums VARCHAR(MAX)
DECLARE #Seq Integer
SELECT
#SizeColums = COALESCE(#SizeColums + ',','') + QUOTENAME([Size]),
#Seq = SEQNO
FROM vw_optimums1
GROUP BY [Size],[SEQNO]
ORDER BY [SEQNO]
DECLARE #Sql NVARCHAR(MAX) = N'
SELECT Whse,
[Types],' +
#SizeColums + '
FROM (SELECT * FROM vw_optimums1) t
UNPIVOT (
[Type]
FOR [Types] IN ([Stock],[LWSALES],[L4WSALES],[ATP]) ) up
PIVOT (
MAX([Type])
FOR [Size] IN (' + #SizeColums + ')
) p
'
EXEC sp_executesql #sql;
About the closest I can get you is this.
DECLARE #SizeColums VARCHAR(MAX)
SELECT #SizeColums = COALESCE(#SizeColums + ',','') + QUOTENAME([Size])
FROM vw_optimums
GROUP BY [Size]
DECLARE #Sql NVARCHAR(MAX) = N'
SELECT Whse,
[Types],' +
#SizeColums + '
FROM (SELECT * FROM vw_optimums
) t
UNPIVOT (
[Type]
FOR [Types] IN ([Stock],[Sales],[4WeekSales],[ATP]) ) up
PIVOT (
MAX([Type])
FOR [Size] IN (' + #SizeColums + ')
) p
'
This actually uses UNPIVOT first to get the breakdown by size, then pivots based on size.
You'll get a result like this based on the sample data.
Whse Types 10 12 14
-------- -------------- ----------- ----------- -----------
London 4WeekSales 6 8
London ATP 7 10
London Sales 3 6
London Stock 2 4
New York 4WeekSales 7 9
New York ATP 5 12
New York Sales 5 8
New York Stock 7 6

Sqlserver PIVOT to turn a "reconstruct" a flat table into columns - why does this not work?

The system we are using allows a data entry form to be created from multiple user defined fields to satisfy information required on a particular group of different "ORDES". The fields are then stored in a database as such from what is entered:
GUID OrderGUID UserDataCode Value
1 100 OrderName Breakfast
2 100 OrderDesc Food you eat before Lunch
3 100 CerealYN Y
4 100 ToastYN Y
5 100 ToastDesc White Bread
6 100 PaperYN Y
7 100 PaperDesc The Newsroom
8 101 OrderName Lunch
9 101 OrderDesc Food you eat before Dinner
10 101 CerealYN N
11 101 ToastYN Y
12 101 ToastDesc Brown Bread
13 101 PaperYN Y
14 101 PaperDesc The MiddayNews
(etc)
(in fact this is an Enterprise Hospital software but I have used simpler examples here)
I would like using SQL to return this table PIVOTed like below
OrderGUID OrderName OrderDESC CerealYN ToastYN ToastDesc ....
101 Breakfast Food you.. Y Y White Bread ....
102 Lunch Food you.. N Y Brown Bread ....
I wrote the following SQL based on examples found on the net:
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME([UserDataCode])
FROM (
SELECT
[UserDataCode]
FROM
[XXX].[dbo].[CV3OrderUserData]
WHERE OrderGUID = 3000680
) AS Codes;
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery = N'SELECT OrderGUID, ' + #ColumnName + '
FROM
[XXX].[dbo].[CV3OrderUserData]
PIVOT(Max(Value)
FOR UserDataCode IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
--SELECT #DynamicPivotQuery
EXEC sp_executesql #DynamicPivotQuery
However while it does the pivot as requested.. and puts the values in the correct new "dynamic" columns, if returns a row for each OrderGUID + Value,
ie:
OrderGUID OrderName OrderDesc CerealYN ToastYN
100 Breakfast null null null ...
100 null Food you.. null null ...
101 null null Y null ...
etc.etc
What am i doing wrong :( ?
The problem in your query is the pivot source query has GUID column which makes the pivot operator to consider GUID column.
To get the expected output you need to remove GUID column from the pivot source query.
Here is a static version you can convert it to dynamic version as you already did.
select * from
(
SELECT OrderGUID,UserDataCode,Value
FROM
tst) A
PIVOT(Max(Value)
FOR UserDataCode IN ([OrderName],[OrderDesc],
[CerealYN],[ToastYN],
[ToastDesc],[PaperYN],
[PaperDesc])) AS PVTTable
SQLFIDDLE DEMO

How to keep column order same in dynamic pivot

I have below mentioned table :
drn RecNum Name Value
----------------------------------------------
1 1 ad1_pk 1
2 1 ad1_address1 P.O. Box 5036
3 1 ad1_address2 NULL
4 1 ad1_address3 NULL
5 1 ad1_ctyfk 56
6 1 ad1_postalcode 80155-5036
7 1 ad1_active Y
8 1 ad1_irstat A
9 1 ad1_irdata NULL
10 1 ad1_at1fk 1
1 2 ad1_pk 2
2 2 ad1_address1 1871 S. Broadway
3 2 ad1_address2 NULL
4 2 ad1_address3 NULL
5 2 ad1_ctyfk 1
6 2 ad1_postalcode 80210
7 2 ad1_active Y
8 2 ad1_irstat A
9 2 ad1_irdata NULL
10 2 ad1_at1fk 1
I am creating the pivot using the below mentioned query:
declare #var nvarchar(max)
declare #sql nvarchar(max)
set #var = stuff((select distinct ',' + name from temp
for xml path('')),1,1,'') -- **this is giving distinct column list but the order of columns get changed..**
set #sql = 'select * from temp
pivot(max(value) for name in (' + #var + ')) as pvt'
exec sp_executesql #sql
Is there a way to keep the order of the columns unchanged? I want the order of columns listed in #var to be same as in the table.
Add a GROUP BY and an ORDER BY clause (to replace the DISTINCT) where you build your column list as follows:
set #var = stuff((select ',' + min(name) from temp GROUP BY drn ORDER BY drn
for xml path('')),1,1,'')
And don't forget the the necessary aggregation (I've used MIN()). Thanks #Ionic.
This is because you're using a DISTINCT in your SELECT query. If you look at the execution plan, you can see DISTINCT SORT operation. This sorts your result based on the DISTINCT columns you specify, in this case it's Name:
To retain the order, you can try this:
set #var = stuff((
select ',' + name
from(
select
name,
drn,
rn = row_number() over(partition by name order by drn)
from temp
)t
where rn = 1
order by drn
for xml path('')),
1,1,'')

Add functionality to query

I have some data in SQL Server as:
att1 att2 att3 att4 att5 ... att205
---------------------------------------
438 498 3625 3645 5000 ... 5000
438 498 3625 3648 5000 ... 5040
438 498 3625 3629 5000 ... 5330
437 501 3625 3626 5000 ... 5040
438 498 3626 3629 5000 ... 5050
I want to know the square root of the sum for each column of data, to do so I do:
CREATE VIEW VIEW_myTable (ID, Value) AS (
SELECT 1, SQRT(SUM(att1)) FROM myTABLE
UNION ALL SELECT 2, SQRT(SUM(att2)) FROM myTABLE
UNION ALL SELECT 3, SQRT(SUM(att3)) FROM myTABLE
UNION ALL SELECT 4, SQRT(SUM(att4)) FROM myTABLE
...
UNION ALL SELECT 205, SQRT(SUM(att205)) FROM myTABLE
) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'myTABLE'
Also would like to ADD a extra result, the number of rows it sumed...
So in the example above there are 5 rows and 205 columns.
How can I take advantage of the scan sql did to the table while suming the elements?
so I do not do something like another SELECT aka.
SELECT COUNT(*) FROM [myTABLE]
In fewer words I want to take advantage of scanning the table in that query...
I was thinking to put Something like SUM(1) somewhere but do not know where....
I used the FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'myTABLE' because I am doing dynamic sql... but the query looks like that...
----------------EDIT----------------------
As answered by #Neil Moss:
if I would have lots of columns, more than 1024... SQL will not support this... Is there a way to overcome this situation?
If you are prepared to accept the query returning a single row, with a SqrtSumAttrX column for each AttrX column, then dynamically constructing the following works just fine:
select
sqrt(sum(attr1)) as SqrtSumAttr1,
sqrt(sum(attr2)) as SqrtSumAttr2,
sqrt(sum(attr3)) as SqrtSumAttr3,
...
sqrt(sum(attr205)) as SqrtSumAttr205,
sum(1) as RowsScanned
from
MyTable
This has the bonus of only scanning the table once, whereas the sample in your question scans it 205 times - once for each union.
Input:
Attr1 Attr2 Attr3
1 2 3
4 5 6
7 8 9
10 11 12
Output:
SqrtSumAttr1 SqrtSumAttr2 SqrtSumAttr3 RowsScanned
4.69041575982343 5.09901951359278 5.47722557505166 4
EDIT post-acceptance
For dynamic construction, try this:
declare #columns nvarchar(max)
declare #sql nvarchar(max)
set #columns = ''
select
#columns = #columns + 'sqrt(sum([' + [COLUMN_NAME] + '])) as SumSqrt' + [COLUMN_NAME] + ','
from
[INFORMATION_SCHEMA].[COLUMNS]
where
TABLE_NAME = 'MyTable'
and
DATA_TYPE in ('int', 'decimal', 'float') -- numerics only (add other datatypes as needed)
order by
ORDINAL_POSITION
set #sql = 'select ' + #columns + 'count(*) as RowsScanned from Results'
exec (#sql)
I've used count(*) rather than sum(1) as suggested by Andriy M as this returns 0 if no rows exist, rather than null.

Resources