How do I get data from an alias column - sql

I'm trying to calculate based on an alias column.
SELECT
Aged, NotAged, Aging
FROM
(SELECT
DATEDIFF(DAY, CASE WHEN Stat = 'HOLD' THEN Created ELSE Opened END,
CASE WHEN Stat = 'Closed' THEN Closed ELSE GETDATE() END) AS Aged,
DATEDIFF(DAY, CASE WHEN Aged <= 25 THEN GETDATE() AS NotAged ELSE GETDATE() END) AS Aging
FROM
DM.Claim
INNER JOIN
DM.LDesc ON LDescKey = LDescKey) data
How do I go about calculating based on an alias column to get NotAged and Aging?
Expected output would be
Aged {1 2 35} NotAged {1 2} Aging {35}

Without sample data and expected results it's hard to say for sure what you want (aggregation?), but you can use CROSS APPLY (VALUES to create a calculated column, and you can even chain them by referring to a previous one. For example.
SELECT
v1.Aged,
v1.NotAged,
v2.Aging
FROM
DM.Claim AS c
INNER JOIN
DM.LDesc AS ld ON ld.LDescKey = c.LDescKey
CROSS APPLY (VALUES (
DATEDIFF(DAY, CASE WHEN Stat = 'HOLD' THEN Created ELSE Opened END,
CASE WHEN Stat = 'Closed' THEN Closed ELSE GETDATE() END,
CASE WHEN Aged <= 25 THEN GETDATE() END
) ) AS v1(Aged, NotAged)
CROSS APPLY (VALUES (
DATEDIFF(DAY, v1.NotAged ELSE GETDATE() END, v1.Aged)
) ) AS v2(Aging);

Charlieface's suggestion to use CROSS APPLY does the trick.
Another option is using CTE (common table expressions), so calculated (and aliased) columns can be used in expressions downstream.
Here is an example with some made up data.
with somedata as (
SELECT
*
FROM ( values
('HOLD', '2022-06-01', '2022-06-02', '2022-07-15'),
('HOLD', '2022-07-01', '2022-07-02', '2022-07-15'),
('Closed', '2022-06-01', '2022-06-02', '2022-07-15'),
('Closed', '2022-07-01', '2022-07-02', '2022-07-15')
) vals (Stat, Created, Opened, Closed)
)
,
precalc as (
select
*,
Aged = DATEDIFF(
DAY,
CASE WHEN Stat = 'HOLD' THEN Created ELSE Opened END,
CASE WHEN Stat = 'Closed' THEN Closed ELSE GETDATE() END
)
from somedata
)
SELECT
Aged,
NotAged = case when Aged <25 then Aged end,
Aging = case when Aged >=25 then Aged end,
*
FROM precalc
Output (20220721)
Aged
NotAged
Aging
Stat
Created
Opened
Closed
Aged
50
NULL
50
HOLD
2022-06-01
2022-06-02
2022-07-15
50
20
20
NULL
HOLD
2022-07-01
2022-07-02
2022-07-15
20
43
NULL
43
Closed
2022-06-01
2022-06-02
2022-07-15
43
13
13
NULL
Closed
2022-07-01
2022-07-02
2022-07-15
13

Related

SqlServer : Number of 'open' issues on a daily basis

I'm getting an error with this query "Msg 102, Level 15, State 1, Line 12
Incorrect syntax near ';'" and have been at it for a few hours now. I'm trying to calculate 'open' issues on given days (ideally between a timeframe but for now just on dates that have entries).
My Data is simplified as:
IssueID, CreationDate, CompletionDate
I'd like to tally open issues which is when Year(CompletionDate)=1900 and they are cumulative while they are open, ie: if yesterday there was 1 issue open and today has 1 issue open as well, then today's count of open is 2. They should drop off once they are resolved (Year(CompletionDate) <> 1900). Please help I think i'm close?
SELECT
x.created_date,
aOpen + Open_Issue - Resolved_Issue as totopen
from(
select
convert(varchar(10), cast(i.CreationDate as date), 101) as created_date,
sum( case when YEAR(i.CompletionDate)='1900' then 1 else 0 end) as aOpen,
sum( case when YEAR(i.CompletionDate)<>'1900' AND (i.CompletionDate >=
i.CreationDate) then 1 else 0 end ) as Open_Issue,
coalesce(tot,0) as Resolved_Issue
FROM Issues i
LEFT JOIN (SELECT count(IssueID) as tot, CompletionDate as resolved
from Issues where YEAR(CompletionDate)<>'1900' group by CompletionDate,
count(IssueID))x
ON i.CreationDate = x.resolved);
UPDATE
I have this returning output correctly on a daily basis only, as in it is not accounting for previous, still open issues (Legacy_Open_Issue) and adding them.
SELECT
created_date,
aOpen_Today + Legacy_Open_Issue - Resolved_Issue as totopen
FROM(
SELECT
convert(varchar(10), cast(i.CreationDate as date), 101) as created_date,
sum( case when YEAR(i.CompletionDate)=1900 then 1 else 0 end) as aOpen_Today,
sum( case when (YEAR(i.CompletionDate)<>1900 AND (i.CompletionDate >= i.CreationDate)) then 1 else 0 end ) as Legacy_Open_Issue,
coalesce(tot,0) as Resolved_Issue
FROM Issues i
LEFT JOIN (
SELECT count(IssueID) as tot, CompletionDate as resolved
FROM Issues
WHERE YEAR(CompletionDate)<>1900 group by CompletionDate
)x ON x.resolved = i.CreationDate
GROUP BY convert(varchar(10), cast(i.CreationDate as date), 101), coalesce(tot,0)
) AS y;
My Data is
IssueID CreationDate CompletionDate
1 1/15/2019 1/1/1900
2 1/16/2019 1/17/2019
3 1/16/2019 1/1/1900
4 1/20/2019 1/21/2019
5 1/28/2019 1/1/1900
6 1/30/2019 1/1/1900
My Output is
created_date totopen
1/15/2019 1
1/16/2019 2
1/20/2019 1
1/28/2019 1
1/30/2019 1
My Output SHOULD be
created_date totopen
1/15/2019 1
1/16/2019 3
1/20/2019 3
1/28/2019 3
1/30/2019 4
thank you for your help
You need to alias the derived table such as:
SELECT
x.created_date,
aOpen + Open_Issue - Resolved_Issue as totopen
from(
select
convert(varchar(10), cast(i.CreationDate as date), 101) as created_date,
sum( case when YEAR(i.CompletionDate)='1900' then 1 else 0 end) as aOpen,
sum( case when YEAR(i.CompletionDate)<>'1900' AND (i.CompletionDate >=
i.CreationDate) then 1 else 0 end ) as Open_Issue,
coalesce(tot,0) as Resolved_Issue
FROM Issues i
LEFT JOIN (SELECT count(IssueID) as tot, CompletionDate as resolved
from Issues where YEAR(CompletionDate)<>'1900' group by CompletionDate,
count(IssueID))x
ON i.CreationDate = x.resolved) as DT;
Derived tables require an alias. You need to add "AS {alias}" to the end of your query. You should also format and line break the code for better legibility.
SELECT
x.created_date,
aOpen + Open_Issue - Resolved_Issue as totopen
from(
select
convert(varchar(10), cast(i.CreationDate as date), 101) as created_date,
sum( case when YEAR(i.CompletionDate)=1900 then 1 else 0 end) as aOpen,
sum( case when YEAR(i.CompletionDate)<>1900 AND (i.CompletionDate >= i.CreationDate) then 1 else 0 end ) as Open_Issue,
coalesce(tot,0) as Resolved_Issue
FROM Issues i
LEFT JOIN (
SELECT count(IssueID) as tot, CompletionDate as resolved
from Issues
where YEAR(CompletionDate)<>1900
group by CompletionDate
)x ON i.CreationDate = x.resolved
group by convert(varchar(10), cast(i.CreationDate as date), 101)
) AS y;
Also, SO generally doesn't do multiple questions per post. I addressed the error/alias issue, but if you have results issues, you should post a new question with sample data and expected results.
To be able to use date ranges and ensure there are no gaps in your output (ie. on dates where no tickets were created), you may want to consider using a Dates fact / reference table like this:
Select d.Date
, count(i.IssueID) as TotalOpen
, sum(case when DateDiff(DD, d.Date, cast(i.CreationDate as date)) = 0 then 1 else 0 end) as NewOpened
, sum(case when DateDiff(DD, d.Date, cast(i.CompletionDate as date)) = 0 then 1 else 0 end) as NewClosed
From Dates d
Left join Issues i
on d.Date between convert(varchar(10), cast(i.CreationDate as date), 101) and
case when YEAR(i.CompletionDate)='1900' then d.Date else i.CompletionDate end
Group by d.Date
Fill the Dates table with all the dates you'd want to display results for (ie. everyday, weekdays) or use a where clause to filter the date range / pattern.

Outer Column reference in an aggregate function of a cross apply

In my query, I am using OUTER APPLY to get employee count in different scenarios like
Number of Employees Joined in each day of a period
Number of Employees Resigned in each day of a period
Number of employees leave on each day of a period... etc
Expected output (From:2017-01-10 to 2017-01-12 ) is
CDATE TOTAL_COUNT JOIN_COUNT RESIGNED _COUNT ...
2017-01-10 1204 10 2
2017-01-11 1212 5 1
2017-01-12 1216 3 0
Below is my query
DECLARE #P_FROM_DATE DATE = '2017-01-01', --From 1st Jan
#P_TO_DATE DATE = '2017-01-10' --to 10th jan
;WITH CTE_DATE
AS
(
SELECT #P_FROM_DATE AS CDATE
UNION ALL
SELECT DATEADD(DAY,1,CDATE)
FROM CTE_DATE
WHERE DATEADD(DAY,1,CDATE) <= #P_TO_DATE
)
SELECT [CDATE]
,[TOTAL_COUNT]
,[JOIN_COUNT]
FROM CTE_DATE
OUTER APPLY (
SELECT COUNT(CASE WHEN [EMP_DOJ] = [CDATE] THEN 1 ELSE NULL END) AS [JOIN_COUNT]
,COUNT(*) AS [TOTAL_COUNT]
,....
,...
FROM [EMPLOYEE_TABLE]
) AS D
But while executing my query, getting the below error.
Msg 8124, Level 16, State 1, Line 18 Multiple columns are specified in
an aggregated expression containing an outer reference. If an
expression being aggregated contains an outer reference, then that
outer reference must be the only column referenced in the expression.
Here the column [JOIN_COUNT] only producing the error, without this column the query is working. But i have more column pending to add like [JOIN_COUNT] (eg Resigned_Count, ...etc )
You do not need an outer apply to achieve this, simply join your CTE_DATE valus to your employee table and use a sum(case when <Conditions met> then 1 else 0 end) with a group by the CDate
select d.CDate
,sum(case when e.Emp_DoJ <= d.CDate
and e.EmployeeResignDate > d.CDate
then 1
else 0
end) as Total_Count
,sum(case when e.Emp_DoJ = d.CDate
then 1
else 0
end) as Join_Count
,sum(case when e.EmployeeResignDate = d.CDate
then 1
else 0
end) as Resign_Count
from CTE_DATE d
left join Employee_Table e
on(d.CDate between e.Emp_DoJ and e.EmployeeResignDate)
group by d.CDate
order by d.CDate

How to count every half hour?

I have a query that its counting every hour, using a pivot table.
How would it be possible to get the count for every 30 minutes?
for example 8:00-8:29,8:30-8:59,9:00-9:29 etc. until 5:00
SELECT CONVERT(varchar(8),start_date,1) AS 'Day',
SUM(CASE WHEN DATEPART(hour,start_date) = 8 THEN 1 ELSE 0 END) as eight ,
SUM(CASE WHEN DATEPART(hour,start_date) = 9 THEN 1 ELSE 0 END) AS nine,
SUM(CASE WHEN DATEPART(hour,start_date) = 10 THEN 1 ELSE 0 END) AS ten,
SUM(CASE WHEN DATEPART(hour,start_date) = 11 THEN 1 ELSE 0 END) AS eleven,
SUM(CASE WHEN DATEPART(hour,start_date) = 12 THEN 1 ELSE 0 END) AS twelve,
SUM(CASE WHEN DATEPART(hour,start_date) = 13 THEN 1 ELSE 0 END) AS one_clock,
SUM(CASE WHEN DATEPART(hour,start_date) = 14 THEN 1 ELSE 0 END) AS two_clock,
SUM(CASE WHEN DATEPART(hour,start_date) = 15 THEN 1 ELSE 0 END) AS three_clock,
SUM(CASE WHEN DATEPART(hour,start_date) = 16 THEN 1 ELSE 0 END) AS four_clock
FROM test
where user_id is not null
GROUP BY CONVERT(varchar(8),start_date,1)
ORDER BY CONVERT(varchar(8),start_date,1)
I use sql server 2012 (version Microsoft SQL Server Management Studio 11.0.3128.0)
Try using iif as below:
SELECT CONVERT(varchar(8),start_date,1) AS 'Day', SUM(iif(DATEPART(hour,start_date) = 8 and
DATEPART(minute,start_date) >= 0 and
DATEPART(minute,start_date) =< 29,1,0)) as eight_tirty
FROM test where user_id is not null GROUP BY
CONVERT(varchar(8),start_date,1) ORDER BY
CONVERT(varchar(8),start_date,1)
To get counts by day and half hour, something like this should work.
SELECT day, half_hour, count(1) AS half_hour_count
FROM (
SELECT
CAST(start_date AS date) AS day,
DATEPART(hh, start_date)
+ 0.5*(DATEPART(n,start_date)/30) AS half_hour
FROM test
WHERE user_id IS NOT NULL
) qry
GROUP BY day, half_hour
ORDER BY day, half_hour;
Formatting the result could be done later.
You need a few things, and then this query just falls together.
First, assuming you need multiple dates, you're going to want what's known as a Calendar Table (hands down, probably the most useful analysis table).
Next, you're going to want either an existing Numbers table if you have one, or just generate the first on the fly:
WITH Halfs AS (SELECT CAST(0 AS INT) m
UNION ALL
SELECT m + 1
FROM Halfs
WHERE m < 24 * 2)
SELECT m
FROM Halfs
(recursive CTE - generates a table with a list of numbers starting at 0).
These two tables will provide the basis for a range query based on the timestamps in your main table. This will make it very easy for the optimizer to bucket rows for whatever aggregation you're doing. That's done by CROSS JOINing the two tables together in a subquery, as well as adding a couple of other derived columns:
WITH Halfs AS (SELECT CAST(0 AS INT) m
UNION ALL
SELECT m + 1
FROM Halfs
WHERE m < 24 * 2)
SELECT calendarDate, m, rangeStart, rangeEnd
FROM (SELECT Calendar.calendarDate, Halfs.m rangeGroup,
DATEADD(minutes, m * 30, CAST(Calendar.calendarDate AS DATETIME2) rangeStart,
DATEADD(minutes, (m + 1) * 30, CAST(Calendar.calendarDate AS DATETIME2) rangeEnd
FROM Calendar
CROSS JOIN Halfs
WHERE Calendar.calendarDate >= CAST('20160823' AS DATE)
AND Calendar.calendarDate < CAST('20160830' AS DATE)
-- OR whatever your date range actually is.
) Range
ORDER BY rangeStart
(note that, if the range of dates is sufficiently large, it may be beneficial to save this off as a temporary table with indicies. For small tables and datasets, the performance gain isn't likely to be noticeable)
Now that we have our ranges, it's trivial to get our groups, and pivot the table.
Oh, and SQL Server has a specific operator for PIVOTing.
WITH Halfs AS (SELECT CAST(0 AS INT) m
UNION ALL
SELECT m + 1
FROM Halfs
WHERE m < 3 * 2)
-- Intentionally limiting range for example only
SELECT calendarDate AS day, [0], [1], [2], [3], [4], [5], [6]
-- If you're displaying "nice" names,
-- do it at this point, or in the reporting application
FROM (SELECT Range.calendarDate, Range.rangeGroup
FROM (SELECT Calendar.calendarDate, Halfs.m rangeGroup,
DATEADD(minutes, m * 30, CAST(Calendar.calendarDate AS DATETIME2) rangeStart,
DATEADD(minutes, (m + 1) * 30, CAST(Calendar.calendarDate AS DATETIME2) rangeEnd
FROM Calendar
CROSS JOIN Halfs
WHERE Calendar.calendarDate >= CAST('20160823' AS DATE)
AND Calendar.calendarDate < CAST('20160830' AS DATE)
-- OR whatever your date range actually is.
) Range
LEFT JOIN Test
ON Test.user_id IS NOT NULL
AND Test.start_date >= Range.rangeStart
AND Test.start_date < Range.rangeEnd
) AS DataTable
PIVOT (COUNT(*)
FOR Range.rangeGroup IN ([0], [1], [2], [3], [4], [5], [6])) AS PT
-- Only covers the first 6 groups,
-- or the first three hours.
ORDER BY day
The pivot should take care of the getting individual columns, and COUNT will automatically resolve null rows. Should be all you need.

More efficient way of grouping rows by hour (using a timestamp)

I'm trying to show a log of daily transactions that take place. My current method is embarrassingly inefficient and I'm sure there is a much better solution. Here is my current query:
select ReaderMACAddress,
count(typeid) as 'Total Transactions',
SUM(CASE WHEN CAST("Timestamp" as TIME) between '05:00:00' and '11:59:59' THEN 1 ELSE 0 END) as 'Morning(5am-12pm)',
SUM(CASE WHEN CAST("Timestamp" as TIME) between '12:00:00' and '17:59:59' THEN 1 ELSE 0 END) as 'AfternoonActivity(12pm-6pm)',
SUM(CASE WHEN CAST("Timestamp" as TIME) between '18:00:00' and '23:59:59' THEN 1 ELSE 0 END) as 'EveningActivity(6pm-12am)',
SUM(CASE WHEN CAST("Timestamp" as TIME) between '00:00:00' and '04:59:59' THEN 1 ELSE 0 END) as 'OtherActivity(12am-5am)'
from Transactions
where ReaderMACAddress = '0014f54033f5'
Group by ReaderMACAddress;
which returns the results:
ReaderMACAddress Total Transactions Morning(5am-12pm) AfternoonActivity(12pm-6pm) EveningActivity(6pm-12am) OtherActivity(12am-5am)
0014f54033f5 932 269 431 232 0
(sorry for any alignment issues here)
At the moment I only want to look at a single Reader that I specify (through the where clause). Ideally, it would be easier to read if the time sections were in a single column and the results, i.e. a count function were in a second column yielding results such as:
Total Transactions 932
Morning(5am-12pm) 269
AfternoonActivity(12pm-6pm) 431
EveningActivity(6pm-12am) 232
OtherActivity(12am-5am) 0
Thanks for any help :)
I would first consider a computed column, but I believe from a previous post you don't have the ability to change the schema. So how about a view?
CREATE VIEW dbo.GroupedReaderView
AS
SELECT ReaderMACAddress,
Slot = CASE WHEN t >= '05:00' AND t < '12:00' THEN 1
WHEN t >= '12:00' AND t < '18:00' THEN 2
WHEN t >= '18:00' THEN 3 ELSE 4 END
FROM
(
SELECT ReaderMACAddress, t = CONVERT(TIME, [Timestamp])
FROM dbo.Transactions
) AS x;
Now your per-MAC address query is much, much simpler:
SELECT Slot, COUNT(*)
FROM dbo.GroupedReaderView
WHERE ReaderMACAddress = '00...'
GROUP BY Slot;
This will provide a result like:
1 269
2 431
3 232
4 0
You can also add WITH ROLLUP which will provide a grand total with the Slot column being NULL:
SELECT Slot, COUNT(*)
FROM dbo.GroupedReaderView
WHERE ReaderMACAddress = '00...'
GROUP BY Slot
WITH ROLLUP;
Should yield:
1 269
2 431
3 232
4 0
NULL 932
And you can pivot that if you need to, add labels per slot, etc. in your presentation tier.
You could also do it this way, it just makes the view a lot more verbose and pulls a lot of extra data when you query it directly; it's also slightly less efficient to group by strings.
CREATE VIEW dbo.GroupedReaderView
AS
SELECT ReaderMACAddress,
Slot = CASE WHEN t >= '05:00' AND t < '12:00' THEN
'Morning(5am-12pm)'
WHEN t >= '12:00' AND t < '18:00' THEN
'Afternoon(12pm-6pm)'
WHEN t >= '18:00' THEN
'Evening(6pm-12am)'
ELSE
'Other(12am-5am)'
END
FROM
(
SELECT ReaderMACAddress, t = CONVERT(TIME, [Timestamp])
FROM dbo.Transactions
) AS x;
These aren't necessarily more efficient than what you've got, but they're less repetitive and easier on the eyes. :-)
Also if you don't want to (or can't) create a view, you can just put that into a subquery, e.g.
SELECT Slot, COUNT(*)
FROM
(
SELECT ReaderMACAddress,
Slot = CASE WHEN t >= '05:00' AND t < '12:00' THEN
'Morning(5am-12pm)'
WHEN t >= '12:00' AND t < '18:00' THEN
'Afternoon(12pm-6pm)'
WHEN t >= '18:00' THEN
'Evening(6pm-12am)'
ELSE
'Other(12am-5am)'
END
FROM
(
SELECT ReaderMACAddress, t = CONVERT(TIME, [Timestamp])
FROM dbo.Transactions
) AS x
) AS y
WHERE ReaderMACAddress = '00...'
GROUP BY Slot
WITH ROLLUP;
Just an alternative that still lets you use BETWEEN and may be even a little less verbose:
SELECT Slot, COUNT(*)
FROM
(
SELECT ReaderMACAddress,
Slot = CASE WHEN h BETWEEN 5 AND 11 THEN 'Morning(5am-12pm)'
WHEN h BETWEEN 12 AND 17 THEN 'Afternoon(12pm-6pm)'
WHEN h >= 18 THEN 'Evening(6pm-12am)'
ELSE 'Other(12am-5am)'
END
FROM
(
SELECT ReaderMACAddress, h = DATEPART(HOUR, [Timestamp])
FROM dbo.Transactions
) AS x
) AS y
WHERE ReaderMACAddress = '00...'
GROUP BY Slot
WITH ROLLUP;
UPDATE
To always include each slot even if there are no results for that slot:
;WITH slots(s, label, h1, h2) AS
(
SELECT 1, 'Morning(5am-12pm)' , 5, 11
UNION ALL SELECT 2, 'Afternoon(12pm-6pm)' , 12, 17
UNION ALL SELECT 3, 'Evening(6pm-12am)' , 18, 23
UNION ALL SELECT 4, 'Other(12am-5am)' , 0, 4
)
SELECT s.label, c = COALESCE(COUNT(y.ReaderMACAddress), 0)
FROM slots AS s
LEFT OUTER JOIN
(
SELECT ReaderMACAddress, h = DATEPART(HOUR, [Timestamp])
FROM dbo.Transactions
WHERE ReaderMACAddress = '00...'
) AS y
ON y.h BETWEEN s.h1 AND s.h2
GROUP BY s.label
WITH ROLLUP;
The key in all of these cases is to simplify and not repeat yourself. Even if SQL Server only performs it once, why convert to time 4+ times?

SQL statement to get record datetime field value as column of result

I have the following two tables
activity(activity_id, title, description, group_id)
statistic(statistic_id, activity_id, date, user_id, result)
group_id and user_id come from active directory. Result is an integer.
Given a user_id and a date range of 6 days (Mon - Sat) which I've calculated on the business logic side, and the fact that some of the dates in the date range may not have a statistic result for the particular date (ie. day1 and day 4 may have entered statistic rows for a particular activity, but there may not be any entries for days 2, 3, 5 and 6) how can I get a SQL result with the following format? Keep in mind that if a particular activity doesn't have a record for the particular date in the statistics table, then that day should return 0 in the SQL result.
activity_id group_id day1result day2result day3result day4result day5result day6 result
----------- -------- ---------- ---------- ---------- ---------- ---------- -----------
sample1 Secured 0 5 1 0 2 1
sample2 Unsecured 1 0 0 4 3 2
Note: Currently I am planning on handling this in the business logic, but that would require multiple queries (one to create a list of distinct activities for that user for the date range, and one for each activity looping through each date for a result or lack of result, to populate the 2nd dimension of the array with date-related results). That could end up with 50+ queries for each user per date range, which seems like overkill to me.
I got this working for 4 days and I can get it working for all 6 days, but it seems like overkill. Is there a way to simplify this?:
SELECT d1d2.activity_id, ISNULL(d1d2.result1,0) AS day1, ISNULL(d1d2.result2,0) AS day2, ISNULL(d3d4.result3,0) AS day3, ISNULL(d3d4.result4,0) AS day4
FROM
(SELECT ISNULL(d1.activity_id,0) AS activity_id, ISNULL(result1,0) AS result1, ISNULL(result2,0) AS result2
FROM
(SELECT ISNULL(statistic_result,0) AS result1, ISNULL(activity_id,0) AS activity_id
FROM statistic
WHERE user_id='jeremiah' AND statistic_date='11/22/2011'
) d1
FROM JOIN
(SELECT ISNULL(statistic_result,0) AS result2, ISNULL(activity_id,0) AS activity_id
FROM statistic WHERE user_id='jeremiah' AND statistic_date='11/23/2011'
) d2
ON d1.activity_id=d2.activity_id
) d1d2
FULL JOIN
(SELECT d3.activity_id AS activity_id, ISNULL(d3.result3,0) AS result3, ISNULL(d4.result4,0) AS result4
FROM
(SELECT ISNULL(statistic_result,0) AS result3, ISNULL(activity_id,0) AS activity_id
FROM statistic WHERE user_id='jeremiah' AND statistic_date='11/24/2011'
) d3
FULL JOIN
(SELECT ISNULL(statistic_result,0) AS result4, ISNULL(activity_id,0) AS activity_id
FROM statistic WHERE user_id='jeremiah' AND statistic_date='11/25/2011'
) d4
ON d3.activity_id=d4.activity_id
) d3d4
ON d1d2.activity_id=d3d4.activity_id
ORDER BY d1d2.activity_id
Here is a typical approach for this kind of thing:
DECLARE #minDate DATETIME,
#maxdate DATETIME,
#userID VARCHAR(200)
SELECT #minDate = '2011-11-15 00:00:00',
#maxDate = '2011-11-22 23:59:59',
#userID = 'jeremiah'
SELECT A.activity_id, A.group_id,
SUM(CASE WHEN DATEDIFF(day, #minDate, S.date) = 0 THEN S.Result ELSE 0 END) AS Day1Result,
SUM(CASE WHEN DATEDIFF(day, #minDate, S.date) = 1 THEN S.Result ELSE 0 END) AS Day2Result,
SUM(CASE WHEN DATEDIFF(day, #minDate, S.date) = 2 THEN S.Result ELSE 0 END) AS Day3Result,
SUM(CASE WHEN DATEDIFF(day, #minDate, S.date) = 3 THEN S.Result ELSE 0 END) AS Day4Result,
SUM(CASE WHEN DATEDIFF(day, #minDate, S.date) = 4 THEN S.Result ELSE 0 END) AS Day5Result,
SUM(CASE WHEN DATEDIFF(day, #minDate, S.date) = 5 THEN S.Result ELSE 0 END) AS Day6Result
FROM activity A
LEFT OUTER JOIN statistic S
ON A.activity_id = S.activity_ID
AND S.user_id = #userID
WHERE S.date between #minDate AND #maxDate
GROUP BY A.activity_id, A.group_id
First, I'm using group by to reduce the resultset to one row per activity_id/group_id, then I'm using CASE to separate values for each individual column. In this case I'm looking at which day in the last seven, but you can use whatever logic there to determine what date. The case statements will return the value of S.result if the row is for that particular day, or 0 if it's not. SUM will add up the individual values (or just the one, if there is only one) and consolidate that into a single row.
You'll also note my date range is based on midnight on the first day in the range and 11:59PM on the last day of the range to ensure all times are included in the range.
Finally, I'm performing a left join so you will always have a 0 in your columns, even if there are no statistics.
I'm not entirely sure how your results are segregated by group in addition to activity (unless group is a higher level construct), but here is the approach I would take:
SELECT activity_id
day1result = SUM(CASE DATEPART(weekday, date) WHEN 1 THEN result ELSE 0 END)
FROM statistic
GROUP BY activity_id
I will leave the rest of the days and addition of group_id to you, but you should see the general approach.