Reputation: 119
Had a tough time just coming up with a title that explains what I'm trying to do.. I've been googling for a while and am not getting anywhere.
Using SQL Server 2008r2 Enterprise
I'm trying to get multiple results (columns) for queries against a single table. The table looks something like this:
sample_id sampletime samplevalue
100 2013-09-07 00:00:00.000 12
101 2013-09-07 00:00:00.000 15
102 2013-09-07 00:00:00.000 11
100 2013-09-07 00:05:00.000 14
101 2013-09-07 00:05:00.000 12
102 2013-09-07 00:05:00.000 13
What I'd like to do is get a averages per sample_id per day/week/etc but have each sample_id be a column. I currently have this query:
select
DATEDIFF(ww, GETDATE(), sampletime) AS weeks_ago,
AVG(samplevalue) AS item1
from table
where
sample_id = '100'
and sampletime between '2013-09-01' AND '2013-10-01'
group by DATEDIFF(ww,getdate(),sampletime)
order by weeks_ago
That gives me the following output:
weeks_ago item1
-5 10.717936
-4 13.009690
-3 11.401884
-2 11.073626
-1 15.417648
0 18.399488
That's exactly how I want the output to look, however the issue is that I need to do this query for dozens of different sample_id's, and I'd really like to get the output to look something like this:
weeks_ago item1 item2 item3
-5 10.717936 11.401884 6.944170
-4 13.009690 10.717936 8.330120
-3 11.401884 18.399488 7.476393
-2 11.073626 15.417648 7.933386
-1 15.417648 13.009690 9.651132
0 18.399488 18.399488 7.456417
And so on... I've tried using 'IN' and including a bunch of sample_id's like so:
select
sample_id,
DATEDIFF(ww, GETDATE(), sampletime) AS weeks_ago,
AVG(samplevalue) AS avg_vol
from table
where
sample_id in ('100','101','102')
and sampletime between '2013-09-01' AND '2013-10-01'
group by DATEDIFF(ww,getdate(),sampletime), sample_id
order by weeks_ago
But that gives me output like this:
sample_id avg_vol weeks_ago
100 6.834470 -4
101 3.235943 -4
102 3.952023 -4
100 10.330120 -3
101 4.753588 -3
102 3.928382 -3
100 1.401884 -2
101 7.476393 -2
102 6.426609 -2
That's not great because it doesn't make it clear how the avg volume has changed for specific items over time.. Not sure if I'm doing a good job of explaining what the problem is but if anyone has any suggestions I'd very much appreciate it!
Solved!
SELECT weeks_ago, [100] as item1, [101] as item2, [102] as item3, [n..]
FROM (
SELECT
sample_id,
DATEDIFF(ww, GETDATE(), sampletime) as weeks_ago,
samplevalue
FROM table
WHERE sample_id in (100,101,102,n...)
AND sampletime between 'YYYY-MM-DD' and 'YYYY-MM-DD'
) main
PIVOT (
AVG(samplevalue) for sample_id in ([100],[101],[102],[n..])
) pvt
Thank you everyone for your help!
Upvotes: 1
Views: 127
Reputation: 21047
I'm not quite familiar with SQL server, but after googling around, I found a solution that might work.
First, to simplify everything, create a temporary table:
select
sample_id,
DATEDIFF(ww, GETDATE(), sampletime) AS weeks_ago,
AVG(samplevalue) AS avg_vol
into #temp_table
from table
where
sample_id in ('100','101','102')
and sampletime between '2013-09-01' AND '2013-10-01'
group by DATEDIFF(ww,getdate(),sampletime), sample_id
order by weeks_ago;
Now work with #temp_table
. First, let's get the unique 'sample_id` values:
DECLARE @DynamicPivotQuery AS NVARCHAR(MAX);
DECLARE @ColumnName AS NVARCHAR(MAX);
SELECT @ColumnName= ISNULL(@ColumnName + ',','')
+ QUOTENAME(sample_id)
FROM (SELECT DISTINCT sample_id FROM #temp_table) AS t;
And now, let's build the pivot table:
--Prepare the PIVOT query using the dynamic
SET @DynamicPivotQuery =
N'SELECT week_ago, ' + @ColumnName + '
FROM #temp_table
PIVOT(SUM(avg_vol)
FOR week_ago IN (' + @ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql @DynamicPivotQuery;
Hope this helps.
Reference:
Upvotes: 0
Reputation: 1438
As Sean Lange mentions in a comment above, it sounds like you want to do a Pivot
on your data so that you can get each item.
An example using your set above:
select weeks_ago, pvt.[100], pvt.[101], pvt.[102]
FROM (
select
sample_id,
DATEDIFF(ww, GETDATE(), sampletime) AS weeks_ago,
samplevalue
from #sample
) main
PIVOT
(
AVG(samplevalue) FOR sample_id in ([100], [101], [102])
) pvt
The disadvantage to Pivot is that, unless you generate this query dynamically, you have to know all of your samples ahead of time, which could get tedious.
Example of dynamic generation:
DECLARE @string nvarchar(max) = '', @sql nvarchar(max) = '';
select @string =
(
select distinct '[' + cast(sample_id as varchar(5)) + '],' from #sample FOR XML PATH('')
)
select @string = LEFT(@string, LEN(@string)-1)
select @string
SELECT @sql =
'
select weeks_ago, '+@string+'
FROM (
select
sample_id,
DATEDIFF(ww, GETDATE(), sampletime) AS weeks_ago,
samplevalue
from #sample
) main
PIVOT
(
AVG(samplevalue) FOR sample_id in ('+@string+')
) pvt
'
EXEC (@sql);
Using dynamic SQL can be tricky, though, and I don't recommend using it if you're not used to using it, or if you're calling code directly (rather than say, a stored procedure) because of the dangers of SQL injection and the like.
Upvotes: 2