Average values given times and make a pivot table - sql-server
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)
Related
Pivoting rows to columns depending on data from another table
I have two tables VisitorsPerDay and Languages as follows: Languages table | Code | Alias | |---------------------|------------------| | EN | English | | AR | Arabic | | FR | French | | JP | Japanese | VisitorsPerDay table | Date | VisitorLanguage | Count | |---------------------|------------------|-------------| | 10/1/2019 | EN | 20 | | 10/1/2019 | EN | 10 | | 10/1/2019 | AR | 5 | | 15/1/2019 | FR | 1 | What the result should be is aggregated data for each day and two columns for each language in the languages table dynamically in which if a new language has been added there will be no need to edit the stored procedure | Date | TotalVisits | En Visits | En AVG Visit % | |---------------------|------------------|-------------|------------------| | 10/1/2019 | 35 | 30 | 85% | | 15/1/2019 | 1 | 0 | 0% | What I have done is created a dynamic query and a cursor that loop over the languages and generate the require SQL statements for each language and append it to the dynamic query What I want to know is there a better way to get the result set or is a dynamic query OK?
You have to use Dynamic SQL, the query will be ugly and not easy to maintain declare #sql nvarchar(max) select #sql = isnull(#sql + ',', 'SELECT [Date], TotalVisits = sum([Count]),' + char(13)) + 'SUM(CASE WHEN VisitorLanguage = ''' + Code + ''' THEN [Count] END) AS [' + Code + ' Visits],'+ char(13) + 'SUM(CASE WHEN VisitorLanguage = ''' + Code + ''' THEN [Count] END) * 100 / SUM([Count]) AS [' + Code + ' AVG Visits %]'+ char(13) from Languages select #sql = #sql + 'FROM VisitorsPerDay GROUP BY [Date]' -- print out the dynmamic query print #sql exec sp_executesql #sql
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 |
Pivoting tables in MS SQL SERVER
I have some data that I want to pivot. --------------------------------------------- | date | price | col_1 | col_2 | --------------------------------------------- | 2017-12-10 | 26 | fruit | apple | | 2017-12-10 | 346 | Vege | carrot | | 2017-12-11 | 644 | Sweet | cake | | 2017-12-11 | 35435 | fruit | banana | | 2017-12-12 | 453455 | veggie| beans | --------------------------------------------- col_1 and col_2 are combinations of categories So basically the output should be 1 row per unique date, and the rest of the columns should be relevant combinations of col_1 and col2 separated by a hyphen. The table will be populated by the total price of the combination of col_1 col_2 item per date. the diagram below might be useful: ------------------------------------------------------------------- | date | fruit - apple| fruit - banana| val n-SubVal n | ------------------------------------------------------------------- | 2017-12-10 | NULL | 56 | and so on | 2017-12-11 | 100 | NULL | | 2017-12-12 | NULL | 900 | | 2017-12-13 | 45 | NULL | | 2017-12-14 | NULL | NULL | -------------------------------------------------------------------- after about 4 hours talking to a friend and research we have come up with this script below. It executes but nothing gets returned except a very odd error. Incorrect syntax near '*some value in either col_1 or col_2*'. the error is supposedly happening either at the first or second declare statement (whichever line MSSQL randomly chooses as its never the same line #). Any help in getting the above output will be appreciated. Thank you very much. declare #dynamicpivotquery as NVARCHAR(MAX) declare #columnname as NVARCHAR(MAX) select #columnname = COALESCE(#columnname + ', ','') + quotename(col_1) + quotename(col_1) from (select distinct col_1, col_2 from *tablename*) as d set #dynamicpivotquery = N'select distinct date, ' + #columnname + ' from *tablename* PIVOT (sum(price) for d in (' + #columnname + ')) as pivot12' EXEC sp_executesql #dynamicpivotquery
Some remarks on your query: The sample data has fieldnames date, price, col_1 and col_2 while your query uses fieldnames of sold_date, sold_price, cat_1 and cat_2. You could have had a look at the result in the variable #dynamicpivotquery to see what's wrong: [fruit][apple] is not a valid fieldname. It should have been [fruit - apple] instead. The PIVOT query did not know the field d with the concatenated category names. The distinct in the dynamic query isn't needed. You have to make sure that only the required fields (sold_date, sold_price and d) are contained in the source for the PICOT command, because otherwise query will also be grouped by all additional fields, which will result in unwanted rows. Your code could work like this: DECLARE #dynamicpivotquery nvarchar(MAX) DECLARE #columnnames nvarchar(MAX) SELECT #columnnames = ISNULL(#columnnames + ', ', '') + QUOTENAME(ISNULL(cat_1, '') + ' - ' + ISNULL(cat_2, '')) FROM (SELECT DISTINCT cat_1, cat_2 FROM tablename) AS d SET #dynamicpivotquery = N'SELECT sold_date, ' + #columnnames + N' FROM (SELECT sold_date, sold_price, ISNULL(cat_1, '''') + '' - '' + ISNULL(cat_2, '''') AS d FROM tablename ) AS src PIVOT (SUM(sold_price) FOR d IN (' + #columnnames + ')) AS pivot12' EXEC sp_executesql #dynamicpivotquery
You can try the following query : declare #dynamicpivotquery as NVARCHAR(MAX) declare #columnname as NVARCHAR(MAX) select #columnname = COALESCE(#columnname + ', ','') + QuoteName(cat) from (select distinct cat_1+'-'+cat_2 as cat from #YourTable) as d SET #dynamicpivotquery = N';WITH p AS ( SELECT sold_date, [cat_1] +''-'' +[cat_2] AS CATCOL, SUM(sold_price) AS sold_price FROM #YourTable GROUP BY sold_date, [cat_1] + ''-'' + [cat_2] ) SELECT sold_date, ' + #columnname + ' FROM p PIVOT (SUM([sold_price]) FOR CATCOL IN (' + #columnname + ')) AS pivotcat12' EXEC sp_executesql #dynamicpivotquery Sample data : sold_date sold_price cat_1 cat_2 cat_12 ---------------------------------------------------- 2017-12-10 26,00 fruit apple fruit-apple 2017-12-10 346,00 vege carrot vege-carrot 2017-12-11 644,00 sweet cake sweet-cake 2017-12-11 35435,00 fruit banana fruit-banana 2017-12-12 453455,00 veggie beans veggie-beans 2017-12-12 100,00 other fruits other-fruits 2017-12-12 100,00 other fruits other-fruits 2017-12-12 100,00 other fruits other-fruits Dynamic query string : ;WITH p AS ( SELECT sold_date, [cat_1] +'-' +[cat_2] AS CATCOL, SUM(sold_price) AS sold_price FROM #YourTable GROUP BY sold_date, [cat_1] + '-' + [cat_2] ) SELECT sold_date, [fruit-apple], [fruit-banana], [other-fruits], [sweet-cake], [vege-carrot], [veggie-beans] FROM p PIVOT ( SUM([sold_price]) FOR CATCOL IN ([fruit-apple], [fruit-banana], [other-fruits], [sweet-cake], [vege-carrot], [veggie-beans])) AS pivotcat12 Result : sold_date fruit-apple fruit-banana other-fruits sweet-cake vege-carrot veggie-beans -------------------------------------------------------------------------------------------- 2017-12-10 26,00 NULL NULL NULL 346,00 NULL 2017-12-11 NULL 35435,00 NULL 644,00 NULL NULL 2017-12-12 NULL NULL 300,00 NULL NULL 453455,00
Pivot The data Twice. Just apply the same logic dynamically so that it appears for all combinations of col_1 and col_2. select * from ( select [DATE], [col_1] ,[col_2],[price] from *tablename* ) TableName pivot (sum(price) for col_1 in (fruit) ) as FirstPivot pivot (sum(fruit) for col_2 in (apple,banana) ) as SecondPivot
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
T-SQL: from rows to columns but not an actual pivot
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