Related
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);
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)
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
I have data in a table in this structure:
Region | Date | Value
A | 01/01/2014 | 100
A | 01/20/2014 | 50
A | 01/02/2014 | 200
A | 01/05/2014 | 300
B | 01/01/2014 | 50
B | 02/15/2014 | 70
B | 02/25/2014 | 50
C | 05/02/2014 | 70
I am trying to create a pivot view like this using T-SQL queries:
Region | Jan-2014 | Feb-2014 | Mar-014 | Apr-2014 | May-2014 | -> thru desired month-year
A | 150 | 200 | 0 | 0 | 300 |
B | 50 | 120 | 0 | 0 | 0 |
C | 0 | 0 | 0 | 0 | 70 |
Please note, multiple values in the same month for a given region needs to be aggregated
Months that have no records should still show up as columns with zero values (Ex: March and April)
I tired using pivot options, withroll up etc., -- but can't seem to get this to work
any help is much appreciated..
Thank you.
The reason why you do not have results for those months is because you are missing the dates that you are pivoting into columns.
There are a few ways that you can do this. You can hard code all of the date values in the IN portion of your query so the columns will appear:
select Region,
isnull([Jan-2014], 0) [Jan-2014], isnull([Feb-2014], 0) [Feb-2014],
isnull([Mar-2014], 0) [Mar-2014], isnull([Apr-2014], 0) [Apr-2014],
isnull([May-2014], 0) [May-2014], isnull([Jun-2014], 0) [Jun-2014],
isnull([Jul-2014], 0) [Jul-2014], isnull([Aug-2014], 0) [Aug-2014],
isnull([Sep-2014], 0) [Sep-2014], isnull([Oct-2014], 0) [Oct-2014],
isnull([Nov-2014], 0) [Nov-2014], isnull([Dec-2014], 0) [Dec-2014]
from
(
select left(datename(month, t.date), 3) +'-'
+ cast(year(t.date) as char(4)) monthYear,
t.region,
t.value
from yt t
) src
pivot
(
sum(value)
for monthYear in ([Jan-2014], [Feb-2014], [Mar-2014], [Apr-2014],
[May-2014], [Jun-2014], [Jul-2014], [Aug-2014],
[Sep-2014], [Oct-2014], [Nov-2014], [Dec-2014])
) piv;
See SQL Fiddle with Demo.
Another way to do this is to create a table of dates that you can use in your query. Once created, then you can use a LEFT JOIN to your table so you return all of the dates that you want to appear. You can either create a recursive query to generate this list in your PIVOT query, or populate a table with a list of dates. A recursive query will be similar to this:
;with dates (startDate, endDate) as
(
select min(date), cast('2014-12-31' as date)
from yt
union all
select dateadd(m, 1, startDate), enddate
from dates
where month(startDate) + 1 <= month(enddate)
)
select startDate
from dates;
See SQL Fiddle with Demo. If you join this to your PIVOT, then the query will be:
select *
from
(
select left(datename(month, d.startdate), 3) +'-'
+ cast(year(d.startdate) as char(4)) monthYear,
t.region,
t.value
from dates d
left join yt t
on month(d.startdate) = month(t.date)
and year(d.startdate) = year(t.date)
) src
pivot
(
sum(value)
for monthYear in ([Jan-2014], [Feb-2014], [Mar-2014], [Apr-2014],
[May-2014], [Jun-2014], [Jul-2014], [Aug-2014],
[Sep-2014], [Oct-2014], [Nov-2014], [Dec-2014])
) piv
where region is not null;
See SQL Fiddle with Demo. You could use this as a part of your PIVOT query, but you would still need to hand-code all of the dates in the IN clause for the PIVOT.
I am guessing that you want to use a dynamic SQL version of this query so the dates will change based on your needs. The dynamic version will be:
DECLARE #cols AS NVARCHAR(MAX),
#colsNull AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#startdate datetime = '2014-01-01',
#enddate datetime = '2014-12-01'
;with dates (startDate, endDate) as
(
select #startdate, #enddate
from yt
union all
select dateadd(m, 1, startDate), enddate
from dates
where dateadd(m, 1, startDate) <= enddate
)
select #cols = STUFF((SELECT ',' + QUOTENAME(left(datename(month, d.startdate), 3) +'-'
+ cast(year(d.startdate) as char(4)))
from dates d
-- where startdate >= '2014-01-01' and startdate <= '2014-06-01'
group by d.startdate
order by d.startdate
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,''),
#colsNull = STUFF((SELECT ', isnull(' + QUOTENAME(left(datename(month, d.startdate), 3) +'-'
+ cast(year(d.startdate) as char(4)))+', 0) as '+QUOTENAME(left(datename(month, d.startdate), 3) +'-'
+ cast(year(d.startdate) as char(4)))
from dates d
-- where startdate >= '2014-01-01' and startdate <= '2014-06-01'
group by d.startdate
order by d.startdate
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
from dates
set #query = 'SELECT region, ' + #colsNull + ' from
(
select left(datename(month, t.date), 3) +''-''
+ cast(year(t.date) as char(4)) monthYear,
t.region,
t.value
from yt t
) x
pivot
(
sum(value)
for monthyear in (' + #cols + ')
) p '
execute(#query);
See SQL Fiddle with Demo. This version uses the recursive CTE to generate the list of dates that will be used in the dynamic sql string. Even though your table of data might not exist for the months displayed, you will still have a new column.
This gives a result:
| REGION | JAN-2014 | FEB-2014 | MAR-2014 | APR-2014 | MAY-2014 | JUN-2014 | JUL-2014 | AUG-2014 | SEP-2014 | OCT-2014 | NOV-2014 | DEC-2014 |
----------------------------------------------------------------------------------------------------------------------------------------------
| A | 650 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| B | 50 | 120 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| C | 0 | 0 | 0 | 0 | 70 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Is there a T-SQL (SQL Server 2008R2) query to transform TABLE_1 into the expected resultset?
TABLE_1
+----------+-------------------------+---------+------+
| IdDevice | Timestamp | M300 | M400 |
+----------+-------------------------+---------+------+
| 3 | 2012-12-05 16:29:51.000 | 2357,69 | 520 |
| 6 | 2012-12-05 16:29:51.000 | 1694,81 | 470 |
| 1 | 2012-12-05 16:29:51.000 | 2046,33 | 111 |
+----------+-------------------------+---------+------+
Expected resultset
+-------------------------+---------+--------+---------+--------+---------+--------+
| Timestamp | 3_M300 | 3_M400 | 6_M300 | 6_M400 | 6_M300 | 6_M400 |
+-------------------------+---------+--------+---------+--------+---------+--------+
| 2012-12-05 16:29:51.000 | 2357,69 | 520 | 1694,81 | 470 | 2046,33 | 111 |
+-------------------------+---------+--------+---------+--------+---------+--------+
This is still a PIVOT query but before you PIVOT you must perform an UNPIVOT of your columns.
First, you perform the UNPIVOT which takes your current multiple columns and transforms them into two columns - one with the value and the other with the column name. The key for the UNPIVOT is that the datatypes be the same, so in the subquery I cast any columns to the same datatype:
select timestamp,
value, cast(iddevice as varchar(10)) + '_'+col as col
from
(
select iddevice,
timestamp,
cast(m300 as varchar(10)) m300,
cast(m400 as varchar(10)) m400
from yourtable
) src
unpivot
(
value
for col in (m300, m400)
) unpiv
See SQL Fiddle with Demo
Result:
| TIMESTAMP | VALUE | COL |
------------------------------------------------------
| December, 05 2012 16:29:51+0000 | 2357,69 | 3_m300 |
| December, 05 2012 16:29:51+0000 | 520 | 3_m400 |
| December, 05 2012 16:29:51+0000 | 1694,81 | 6_m300 |
| December, 05 2012 16:29:51+0000 | 470 | 6_m400 |
| December, 05 2012 16:29:51+0000 | 2046,33 | 1_m300 |
| December, 05 2012 16:29:51+0000 | 111 | 1_m400 |
Once you complete the unpivot, then you can apply the PIVOT function:
select *
from
(
select timestamp,
value, cast(iddevice as varchar(10)) + '_'+col as col
from
(
select iddevice,
timestamp,
cast(m300 as varchar(10)) m300,
cast(m400 as varchar(10)) m400
from yourtable
) src
unpivot
(
value
for col in (m300, m400)
) unpiv
) src1
pivot
(
max(value)
for col in ([3_m300], [3_m400],
[6_m300], [6_m400],
[1_m300], [1_m400])
) piv
See SQL Fiddle with Demo
Results:
| TIMESTAMP | 3_M300 | 3_M400 | 6_M300 | 6_M400 | 1_M300 | 1_M400 |
--------------------------------------------------------------------------------------------
| December, 05 2012 16:29:51+0000 | 2357,69 | 520 | 1694,81 | 470 | 2046,33 | 111 |
If you have an unknown number of IdDevices that you want to transform into columns, then you can use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT DISTINCT ','
+ quotename(cast(t.IdDevice as varchar(10)) +'_'
+c.name)
from yourtable t
cross apply sys.columns as C
where C.object_id = object_id('yourtable') and
C.name not in ('IdDevice', 'Timestamp')
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT timestamp,' + #cols + ' from
(
select timestamp,
value, cast(iddevice as varchar(10)) + ''_''+col as col
from
(
select iddevice,
timestamp,
cast(m300 as varchar(10)) m300,
cast(m400 as varchar(10)) m400
from yourtable
) src
unpivot
(
value
for col in (m300, m400)
) unpiv
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
Edit, if you need a totals field for each m value, then you can use:
select timestamp,
[3_m300], [3_m400],
[6_m300], [6_m400],
[1_m300], [1_m400],
[1_m300] + [3_m300] + [6_m300] Total_m300,
[1_m400] + [3_m400] + [6_m400] Total_m400
from
(
select timestamp,
value, cast(iddevice as varchar(10)) + '_'+col as col
from
(
select iddevice,
timestamp,
m300,
m400
from yourtable
) src
unpivot
(
value
for col in (m300, m400)
) unpiv
) src1
pivot
(
sum(value)
for col in ([3_m300], [3_m400],
[6_m300], [6_m400],
[1_m300], [1_m400])
) piv
See SQL Fiddle with Demo