SQL Server with non-alphabetic value as column name - sql-server

I am building a pivot query inside a CTE. I have a table Table_1:
Store Count xCount
------- ---- ------
101 1 138
109 1 59
101 2 282
109 2 97
105 3 60
109 3 87
105 4.a 60
109 4.b 87
In Table_1, datatype of column count is varchar(10).
I used dynamic pivot query to pivot Table_1
DECLARE #DynamicCol AS NVARCHAR(MAX),
#SQL AS NVARCHAR(MAX)
select #DynamicCol = STUFF((SELECT distinct ',' + QUOTENAME(count)
from table_1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #SQL = ';WITH CTE as (
SELECT store,' + #DynamicCol + ' from
(
select * from table_1
) res
pivot
(
MAX(xCount)
for Count in (' + #DynamicCol + ')
) piv ) SELECT *
FROM CTE where 4.a is null'
execute(#SQL);
and get result as :
| STORE | 1 | 2 | 3 | 4.a |
+-------+-----+-----+-----+-----+
| 101 | 138 | 282 | null| null|
| 105 | null| null| 60 | 60 |
| 109 | 59 | 97 | 87 | 87 |
Now, I tried to get data from column 3 and 4.a where 3 and 4.a is null.
The query I build to get data is
Select * from CTE where 3 is null
Select * from CTE where 4.a is null
Also i tried to use this inside case statement as :
Select *,case when (3 is null) then 'some result' else '' end from CTE
In every query I am not getting any value returned by queries.
I tried by to append 'X' in each pivoted column and remove '.' from column anme, like column name looks like
| STORE | X1 | X2 | X3 | X4a |
+-------+-----+-----+-----+-----+
| 101 | 138 | 282 | null| null|
| 105 | null| null| 60 | 60 |
| 109 | 59 | 97 | 87 | 87 |
I am not able to query for this. Could anyone help me or suggest me any other idea to get data using above mentioned query ?

You have to wrap identifier that starts with digit with []:
DECLARE #DynamicCol AS NVARCHAR(MAX),
#SQL AS NVARCHAR(MAX)
select #DynamicCol = STUFF((SELECT distinct ',' + QUOTENAME(count)
from table_1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #SQL = ';WITH CTE as (
SELECT store,' + #DynamicCol + ' from
(
select * from table_1
) res
pivot
(
MAX(xCount)
for Count in (' + #DynamicCol + ')
) piv ) SELECT *
FROM CTE where [4.a] is null' -- here
execute(#SQL);

Related

Convert Access TRANSFORM/PIVOT query to SQL Server with multiple table [duplicate]

TRANSFORM Avg(CASE WHEN [temp].[sumUnits] > 0
THEN [temp].[SumAvgRent] / [temp].[sumUnits]
ELSE 0
END) AS Expr1
SELECT [temp].[Description]
FROM [temp]
GROUP BY [temp].[Description]
PIVOT [temp].[Period];
Need to convert this query for sql server
I have read all other posts but unable to convert this into the same
Here is the equivalent version using the PIVOT table operator:
SELECT *
FROM
(
SELECT
CASE
WHEN sumUnits > 0
THEN SumAvgRent / sumUnits ELSE 0
END AS Expr1,
Description,
Period
FROM temp
) t
PIVOT
(
AVG(Expr1)
FOR Period IN(Period1, Period2, Period3)
) p;
SQL Fiddle Demo
For instance, this will give you:
| DESCRIPTION | PERIOD1 | PERIOD2 | PERIOD3 |
---------------------------------------------
| D1 | 10 | 0 | 20 |
| D2 | 100 | 1000 | 0 |
| D3 | 50 | 10 | 2 |
Note that When using the MS SQL Server PIVOT table operator, you have to enter the values for the pivoted column. However, IN MS Access, This was the work that TRANSFORM with PIVOT do, which is getting the values of the pivoted column dynamically. In this case you have to do this dynamically with the PIVOT operator, like so:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
SELECT #cols = STUFF((SELECT distinct
',' +
QUOTENAME(Period)
FROM temp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
SET #query = ' SELECT Description, ' + #cols + '
FROM
(
SELECT
CASE
WHEN sumUnits > 0
THEN SumAvgRent / sumUnits ELSE 0
END AS Expr1,
Description,
Period
FROM temp
) t
PIVOT
(
AVG(Expr1)
FOR Period IN( ' + #cols + ')
) p ';
Execute(#query);
Updated SQL Fiddle Demo
This should give you the same result:
| DESCRIPTION | PERIOD1 | PERIOD2 | PERIOD3 |
---------------------------------------------
| D1 | 10 | 0 | 20 |
| D2 | 100 | 1000 | 0 |
| D3 | 50 | 10 | 2 |

Pivot Rows to Column By Group in SQL Server

I am looking for a code which can transpose row to column using the group by in the SQL Server 2008.
This is my table :
Name | Stdy | Val
-------+---------+-------
Kunjan | Technic | 80
Kunjan | Sains | 90
Kunjan | Sport | 60
Shone | Technic | 60
Shone | Sains | 80
Shone | Sport | 70
Peudd | Technic | 85
Peudd | Sains | 75
Peudd | Sport | 90
What I want to eventually display is something like this (for the data above):
Stdy | Kunjan | Shone | Peudd
--------+--------+-------+-------
Technic | 80 | 60 | 85
Sains | 90 | 80 | 75
Sport | 60 | 70 | 90
Any help would be appreciated.
Thanks in advance.
this can be done using PIVOT operator
select *
from yourtable
pivot
(
max(Val)
for Name in ([Kunjan], [Shone], [Peudd])
) p
or conditional CASE statement. There are lots of example, just do a search on it
EDIT : for dynamic case
declare #sql nvarchar(max),
#col nvarchar(max)
select #col = isnull(#col + ',', '') + Name
from yourtable
group by Name
print #col
select #sql =
'
select *
from youtable
pivot
(
max(Val)
for Name in (' + #col + ')
) p
'
print #sql
exec sp_executesql #sql

SQL Server Transpose Rows into Columns using IDs as Column Names

I have a large file that has the following fields:
Table 1:
+---------+--------+-----------+
| User_Id | Key_Id | Value |
+---------+--------+-----------+
| 100 | 74 | 37 |
| 100 | 65 | Male |
| 100 | 279 | G235467 |
+---------+--------+-----------+
and I have another file that tells what each 'Key_Id' is called (they are column names) e.g.
Table 2:
+--------+------------------+
| Key_Id | Key |
+--------+------------------+
| 65 | Gender |
| 66 | Height |
| 74 | Age |
| 279 | ReferenceNo |
I want to create a table using the Key_Id names found in the Key column of table 2, transpose all of the values from table 1 into table 2, but also include the User_Id from table 1 as this relates to an individual.
PS. Table 2 has nearly 300 keys that would need turning into individual fields
So ultimately I would like a table that looks like this:
+---------+---------+--------+-------+--------------+--------+
| User_Id | Gender | Height | Age | ReferenceNo | etc |
+---------+---------+--------+-------+--------------+--------+
| 100 | Male | | 37 | G235467 | |
So that each User_Id is a row and that all the Keys are columns with their respective values
You can use a dynamic sql query as below.
Query
declare #sql as varchar(max);
select #sql = 'select t1.[User_Id], ' + stuff((select +
', max(case t2.[Key_Id] when ' + cast([Key_Id] as varchar(100)) +
' then t1.[Value] end) as [' + [Key] + '] '
from Table2
for xml path('')
), 1, 2, '') +
'from Table1 t1 left join Table2 t2 on t1.[Key_Id] = t2.[Key_Id] group by t1.[User_Id];'
exec(#sql);
Find a demo here
You need to get a coma-separated list of those 300 key names to be used in PIVOT/UNPIVOT operators in T-SQL like described here
https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot
you can use pivot as below:
Select * from (
Select u.UserId, k.[key], u.[Value] from table1 u
join table2 k on u.keyid = k.keyid ) a
pivot ( max([Value]) for [key] in ([Gender], [Height], [Age], [ReferenceNo]) ) p
For dynamic list of keys you can use dynamic sql as below:
Declare #cols1 varchar(max)
Declare #query nvarchar(max)
Select #cols1 = stuff((select ','+QuoteName([Key]) from table2 group by [Key] for xml path('')),1,1,'')
Set #Query = 'Select * from (
Select u.UserId, k.[key], u.[Value] from table1 u
join table2 k on u.keyid = k.keyid ) a
pivot ( max([Value]) for [key] in (' + #cols1 + ') ) p '
Select #Query --Check the generated query and execute by uncommenting below query
--exec sp_executesql #Query

Average values given times and make a pivot table

I have a table like this:
time | sensor | value |
---------------------------
10:00:00 | 1 | 12.3 |
10:00:00 | 2 | 14.3 |
10:00:00 | 3 | 15.3 |
10:00:01 | 1 | 15.3 |
10:00:02 | 2 | 11.3 |
10:00:01 | 3 | 10.3 |
10:00:03 | 1 | 9.3 |
10:00:03 | 2 | 15.3 |
10:00:04 | 3 | 11.3 |
...
10:05:01 | 1 | 15.3 |
10:05:02 | 2 | 11.3 |
10:05:01 | 3 | 10.3 |
10:05:03 | 1 | 9.3 |
10:05:03 | 2 | 15.3 |
10:05:04 | 3 | 11.3 |
And I need a Pivot table where the measurements are averaged in time intervals of lets say one minute, and ordered by sensor:
time | sensor 1| sensor 2| sensor 3|
----------------------------------------
10:00:00 | 11 | 12 | 10 |
10:01:00 | 10 | 13 | 15 |
...
I learned how to do this in MySQL, but now I am forced to use SQL Server and apparently this is completely different to MySQL.
Any help is appreciated, also some non trivial tutorial on this would be helpful.
EDIT
Attempt after the reply from Giorgi Nakeuri:
DECLARE #cols VARCHAR(MAX)
SELECT STUFF((SELECT '],[' + CAST([Group] AS VARCHAR(10)) FROM tblGroups
GROUP BY [Group]
FOR XML PATH('')), 1, 2, '') + ']'
DECLARE #s VARCHAR(MAX) = 'with cte as(
SELECT tblData.Timestmp, tblSeries.GroupID as SensorID, tblData.SeriesID, tblData.DataValue
FROM tblData
INNER JOIN tblSeries ON tblData.SeriesID = tblSeries.SeriesID
WHERE (tblSeries.[SeriesName] = ' + 'ActivePower'
+ 'AND tblData.Timestmp > ' + '2015-02-05 00:00:00' + ' AND tblData.Timestmp < ' + '2015-02-05 23:59:59'
+ 'AND tblData.DataValue>100)
)
select * from cte
pivot(AVG(DataValue) for GroupID in(' + #cols + ' )) p'
EXEC(#s)
The result is:
[150],[151],[152],[154],[159],[160],[164],[165],[166],[167],[168],[169],[171],[172],[173],[174],[175],[176],[180],[181],[182],[184],[185],[186],[187],[188],[189],[191],[192],[193],[194],[195],[196],[197],[20],[201],[205],[206],[207],[208],[209],[21],[210],[217],[218],[219],[220],[221],[222],[223],[224],[225],[226],[227],[228],[229],[231],[232],[233],[236],[237],[249],[251],[252],[253],[254],[258],[259],[260],[262],[263],[276],[277],[278],[279],[281],[288],[289],[290],[291],[293],[294],[295],[296],[300],[301],[302],[304],[308],[309],[314],[315],[316],[317],[324],[326],[329],[330],[331],[332],[333],[334],[335],[339],[340],[344],[347],[348],[351],[352],[353],[354],[355],[356],[357],[359],[370],[372],[373],[374],[375],[376],[380],[381],[382],[383],[384],[385],[386],[387],[388],[389],[390],[394],[395],[396],[397],[398],[400],[404],[405],[406],[407],[408],[409],[410],[411],[412],[413],[414],[418],[419],[420],[421],[425],[432],[435],[436],[437],[438],[439],[443],[444],[445],[452],[453],[457],[465],[466],[467],[468],[484],[485],[486],[487],[494],[495],[498],[499],[501],[506],[507],[511],[515],[516],[520],[521],[523],[527],[530],[531],[532],[533],[548],[550],[601],[605],[614],[615],[617],[81],[82],[829]
This are the sensor ID's, but not the Pivot table...
Try this:
select * from someTable
pivot(AVG(value) for sensor in([1],[2],[3])) p
If you have more columns in your table that you have showed then:
with cte as(select time, sensor, value from someTable)
select * from cte
pivot(AVG(value) for sensor in([1],[2],[3])) p
Dynamic version:
DECLARE #cols VARCHAR(MAX) = ''
SELECT #cols = STUFF((SELECT '],[' + CAST([Group] AS VARCHAR(10)) FROM tblGroups
WHERE [Group] <> 'meteo'
GROUP BY [Group]
FOR XML PATH('')), 1, 2, '') + ']'
DECLARE #s VARCHAR(MAX) = 'with cte as(
SELECT DATEADD(mi, datediff(mi, 0, tblData.Timestmp), 0) Timestmp, tblSeries.GroupID as SensorID, tblData.DataValue
FROM tblData INNER JOIN tblSeries ON tblData.SeriesID = tblSeries.SeriesID
WHERE tblSeries.[SeriesName] = ' + '''ActivePower''' + ' AND tblData.Timestmp > ''' + '2015-02-05 00:00:00''' + '
AND tblData.Timestmp < ' + '''2015-02-05 23:59:59''' + ' AND tblData.DataValue>100
)
select * from cte
pivot(AVG(DataValue) for GroupID in(' + #cols + ' )) p'
EXEC( #s)

Dynamic columns generation on the basis of rows of a table in sql

I have a table with four columns item_id, color, size, weight, I want to show my table rows into one row like item1,color1,size1,weight1,item2,color2,...........,item4,color4,size4,weight4 ...
Following is my table
+---------+--------+--------+--------+
| item_id | color | size | weight |
+---------+--------+--------+--------+
| 1 | blue | large | 65 |
| 2 | orange | large | 57 |
| 3 | red | small | 12 |
| 4 | violet | medium | 34 |
My desired result will be
+---------+--------+--------+--------++---------+--------+--------+
| item_id1| color1| size1 | weight1| item_id2 | color2 | size2 | weight2 |....
+---------+--------+--------+--------+---------+--------+--------+---------------
| 1 | blue | large| 65 | 2 | orange | large | 57 |...
+---------+--------+--------+--------+ +---------+--------+--------+--------+
Thanks in advance.
In order to get this result, you will need to do a few things:
UNPIVOT the current data
PIVOT the result from the unpivot
use dynamic SQL since you will have an unknown number of rows
Since you are using SQL Server 2005+ you can use CROSS APPLY to unpivot the data, this process takes your multiple columns of item_id, color, size and weight and converts them into multiple rows:
select col+'_'+cast(seq as varchar(50)) col,
value
from
(
select item_id as seq, item_id, color, size, weight
from yourtable
) d
cross apply
(
values
('item_id', cast(item_id as varchar(50))),
('color', color),
('size', size),
('weight', cast(weight as varchar(50)))
) c (col, value);
See SQL Fiddle with Demo. This gives a result:
| COL | VALUE |
----------------------
| item_id_1 | 1 |
| color_1 | blue |
| size_1 | large |
| weight_1 | 65 |
| item_id_2 | 2 |
| color_2 | orange |
| size_2 | large |
| weight_2 | 57 |
| item_id_3 | 3 |
As you can see from the result you now have multiple rows in based off your original data. The COL values are the values that you will use to PIVOT. The full dynamic SQL code will be similar to the following:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col+'_'+cast(item_id as varchar(10)))
from yourtable
cross apply
(
select 'item_id', 0 union all
select 'color', 1 union all
select 'size', 2 union all
select 'weight', 3
) c (col, so)
group by item_id, col, so
order by item_id, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + '
from
(
select col+''_''+cast(seq as varchar(50)) col,
value
from
(
select item_id as seq, item_id, color, size, weight
from yourtable
) d
cross apply
(
values
(''item_id'', cast(item_id as varchar(50))),
(''color'', color),
(''size'', size),
(''weight'', cast(weight as varchar(50)))
) c (col, value)
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute(#query);
See SQL Fiddle with Demo. The final result is:
| ITEM_ID_1 | COLOR_1 | SIZE_1 | WEIGHT_1 | ITEM_ID_2 | COLOR_2 | SIZE_2 | WEIGHT_2 | ITEM_ID_3 | COLOR_3 | SIZE_3 | WEIGHT_3 | ITEM_ID_4 | COLOR_4 | SIZE_4 | WEIGHT_4 |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | blue | large | 65 | 2 | orange | large | 57 | 3 | red | small | 12 | 4 | violet | medium | 34 |
If you want to do it programmatically and you don't know the number of rows try this :
DECLARE #I INT, #END INT, #DATA nvarchar(max), #TEMPSTR nvarchar(max), #DynamicTableSQL nvarchar(max)
SET #I = 1
SET #DATA = ''
SET #TEMPSTR = ''
SELECT #END = MAX(item_id) from items
SET #DynamicTableSQL = 'DECLARE #DynamicTable TABLE('
WHILE #I <= #END
BEGIN
--SELECT #I
SET #TEMPSTR = (select CAST(item_id as nvarchar) + ',''' + color + ''',''' + size + ''',' + cast(weight as nvarchar) + ',' FROM items WHERE item_id = #I)
SET #DynamicTableSQL = #DynamicTableSQL + 'item_id_' + CAST(#I AS VARCHAR(10))+ ' INT ,' + 'color_' + CAST(#I AS VARCHAR(10))+ ' NVARCHAR(15) ,'+ 'size_' + CAST(#I AS VARCHAR(10))+ ' NVARCHAR(15) ,'+ 'weight_' + CAST(#I AS VARCHAR(10))+ ' INT ,'
SET #DATA += #TEMPSTR
SELECT #I = #I + 1
END
SET #DynamicTableSQL = SUBSTRING(#DynamicTableSQL, 0, LEN(#DynamicTableSQL))
SET #DynamicTableSQL = #DynamicTableSQL + ') '
SET #DATA = SUBSTRING(#DATA, 0, LEN(#DATA))
SET #DynamicTableSQL = #DynamicTableSQL + ' INSERT INTO #DynamicTable VALUES (' + #DATA + ')'
SET #DynamicTableSQL = #DynamicTableSQL + ' SELECT * FROM #DynamicTable '
EXEC SP_EXECUTESQL #DynamicTableSQL

Resources