I am trying to write ONE SQL query, which gives always gives three rows of results. Database is as follows:
uid | program_date | program_time | program_name
------------------------------------------------
1 | 2012-04-16 | 21:00 | Some movie
2 | 2012-04-16 | 23:00 | Program end
3 | 2012-04-17 | 10:00 | Animation
4 | 2012-04-17 | 11:00 | Some other movie
5 | 2012-04-17 | 12:00 | Some show
All I need - always have three rows - what is on air now, next and upcomming. So if today is 2012-04-16 21:00 it should output Some movie, Program end, Animation.
At 2012-04-17 00:00 it should output Program end, Animation, Some other movie.
Problem is that I need to "navigate" back in one day if there is no records WHERE program_date = date("Y-m-d") AND program_time <= date("H:i:s");
There is another problem - database does not have Unix timestamp field, only Uid, program_date (date field) and program_time (time field) and program_name.
Also, there might be, that Uid's are not inserted into table in sequence, as some program entry might be inserted in between into existing program schedule.
I am trying various approaches, but want to do everything in one SQL query, without looping in PHP.
Can anyone help me here?
As TV-people count and show time in rather strange manner, MySQL function may be created to handle their non-human ;-) logic easier:
CREATE FUNCTION TV_DATE(d CHAR(10), t CHAR(5))
RETURNS CHAR(16) DETERMINISTIC
RETURN CONCAT(d, IF (t < "06:00", "N", " "), t);
User-defined functions are declared per-database and this may be done just once. DETERMINISTIC tells that function always return the same result for the same input and internal MySQL optimizer may rely on that. N is just a letter which is larger (in string comparison) than whitespace. Consider it as mnemonics for next or night.
note: Hours should be always formatted with 2 digits!
Then using this function we may select what we need even simpler:
-- what is on air now
(SELECT `program_name`, TV_DATE(`program_date`, `program_time`) AS `tv_time`
FROM `table`
WHERE (`tv_time` <= TV_DATE(date("Y-m-d"), date("H:i"))
ORDER BY `tv_time` DESC
LIMIT 1)
UNION
-- next and upcomming
(SELECT `program_name`, TV_DATE(`program_date`, `program_time`) AS `tv_time`
FROM `table`
WHERE (`tv_time` > TV_DATE(date("Y-m-d"), date("H:i"))
ORDER BY `tv_time` ASC
LIMIT 0, 2)
Keep in mind, that if all records in DB are in future you'll get only 2 of them.
The same for situation, when the next program is the last one in DB.
You may add different constant values into queries in order to distinguish those 2 situations.
Related
Is there a way to order my SQL results after they have been delivered? So far I have two values. My code currently returns the results and can sort by the dates ORDER BY date ASC
The database entries of interest are.
| date | weeks |
| 2020-07-01 | 2 |
| 2020-07-01 | 3 |
| 2020-07-01 | 5 |
Weeks represents how many weeks away the next date is. so 2020-07-21, 2020-08-03, and so on (I am able to return these values). The order by needs to be able to order the dates and take into account the week value from the closest week to the furthest.
I have tried just updating the database with a new field called final_date, although this makes everything cluttery.
When the data is returned, I can find the new date using:
$addWeeks = strtotime('+'.$numberOfWeeks.' weeks', strtotime($last));
Although, I cannot find a way to order the results by that new value.
The order by needs to be able to order the dates and take into account the week value from the closest week to the furthest.
I think you want:
select t.*
from mytable t
order by t.date + interval t.weeks week
Note that this assumes that column date is of date datatype, not string; else, you need to convert it to a date first, using str_to_date() for example.
I need a solution for my project..Actually, I got trapped in a problem..Let me show you the picture of my problem.
Senerio of my problem
If you can see, there are some columns of 12 months.So I need to run a MySQL query or a program which can show me the selected columns which contain the text "Not Paid".
For example.
If I want to check the members between January to March or June to November etc. who are "Not Paid"..so what is the solution for that...??
I know that we can use the "between" clause but I am not getting the idea..Because I want to use two drops down menu where the user can select from which month to which month they want to see.
Please help me to get rid out of this problem..I am a new beginner in PHP.
If I were you I would represent the months as dates, even if strings (e.g., '2017-06') and then map the month to the semantic representation string (e.g., 'June'). So one way is to store the date in your MySQL database as a varchar.
In terms of modeling the data in your database, I would create a table with three columns 1) the user_id; 2) the due_date; 3) the payment_status.
.----------------------------.
| 0 | 2017-06 | paid |
| 1 | 2017-07 | not paid |
| 2 | 2017-08 | paid |
| 3 | 2017-09 | paid |
'----------------------------'
Now you can write a query to retrieve the members between January to March who has "Not Paid". (note: this is just one way, there are many ways to query this)
SELECT
DISTINCT(user_id)
FROM payments_table
WHERE due_date >= '2017-03'
AND due_date < '2017-06'
AND payment_status = 'not paid';
This question already has answers here:
mysql query room availability
(2 answers)
Closed 7 years ago.
Would like some logical help on formulating a MYSQL Query that gets results that isn't within the data of the table.
I have a table named schedule that has columns with data type 'time' that indicates when this certain schedule starts and ends and a foreign key referencing from table 'rooms' in which the schedule will take place. And in the php code in its search feature, I wanted to add a feature that shows results of rooms that are currently not being occupied by a schedule or is vacant. I added a jquery slider to specifically fetch the start time and end time the searcher wanted.
TABLE 'schedule'
room sched_start sched_end
1 09:00:00 10:00:00
1 11:00:00 12:00:00
2 07:30:00 08:30:00
2 11:30:00 13:00:00
For example, the searcher wanted to search a vacant room from 10:00:00 to 11:00:00. Basing from the database, the result should show that both rooms, room 1 and room 2, should be displayed in the search result as both rooms won't be occupied within the specified time of the searcher. I was thinking of comparing chronologically the schedule of all the similar rooms, the 'sched_end' of the first row or the first schedule and the sched_start of the succeeding row or the schedule and so on, so to determine whether there is a vacant time in between. Can anyone help me on this?
All helps and hates would be very much appreciated as I can be as much noob in MySQL-ing.
DROP TABLE IF EXISTS schedule;
CREATE TABLE schedule
(room INT NOT NULL
,schedule_start TIME NOT NULL
,schedule_end TIME NOT NULL
,PRIMARY KEY(room,schedule_start)
);
INSERT INTO schedule VALUES
(1,'09:00:00','10:00:00'),
(1,'11:00:00','12:00:00'),
(2,'07:30:00','08:30:00'),
(2,'11:30:00','13:00:00'),
(3,'09:30:00','10:30:00'),
(3,'11:00:00','12:00:00'),
(4,'10:30:00','10:45:00');
SET #start:= '10:00:00';
SET #end:= '11:00:00';
SELECT DISTINCT x.room
-- or whatever columns you want from whichever table you want
FROM schedule x
LEFT
JOIN schedule y
ON y.room = x.room
AND y.schedule_start < #end
AND y.schedule_end > #start
-- other tables can join in here
WHERE y.room IS NULL;
+------+
| room |
+------+
| 1 |
| 2 |
+------+
http://sqlfiddle.com/#!9/1b677/1
Just to demonstrate that #M0rtiis's solution is wrong...
SELECT DISTINCT room
FROM schedule
WHERE #end <= schedule_start
OR #start >= schedule_end;
+------+
| room |
+------+
| 1 |
| 2 |
| 3 |
+------+
What you need is to specifically exclude the rooms that are occupied in the given period.
SET #start = '10:00:01';
SET #end = '10:59:59';
SELECT *
FROM `schedule` -- you probably want to select from rooms here...
WHERE room NOT IN (
SELECT room
FROM `schedule`
WHERE sched_start BETWEEN #start AND #end
OR sched_end BETWEEN #start AND #end
OR #start BETWEEN sched_start AND sched_end
OR #end BETWEEN sched_start AND sched_end
);
Note that I compensated the "start inclusive" behaviour by adding one second to the start time and subtracting one second from the end time. You should do that before you feed the times to SQL, to avoid those calculations there.
This query filters all cases, including overlapping meetings.
Or, perhaps slightly more coherently:
SET #start:= '10:00:00';
SET #end:= '11:00:00';
SELECT DISTINCT room
FROM schedule
WHERE room NOT IN ( SELECT room
FROM schedule
WHERE schedule_start < #end
AND schedule_end > #start );
Also, you really need proper indexes if this query is to perform with more than just a couple of rows. Use the EXPLAIN function to help you.
Its bad idea to store there TIME. use DATETIME instead to cover cases where need_start - one day and need_end - another (next? or i want to be guest in your hotel for a week?) day.
But anyway, on what u have now try this
SELECT DISTINCT
room
FROM schedule
WHERE
'11:00:00' <= sched_start
OR
'10:00:00' >= sched_end
http://sqlfiddle.com/#!9/dafae/9
You can use BETWEEN operator.
SELECT *
FROM schedule
WHERE sched_end BETWEEN '10:00:00' AND '11:00:00'
Suppose I have a MySQL table that looks like the following, where I keep track of when (Date) a user (User.id) read an article on my website (Article.id):
------------------------------------------
Article_Impressions
------------------------------------------
date | user_id | article_id
--------------------+---------+-----------
2013-04-02 15:33:23 | 815 | 2342
2013-04-02 15:38:21 | 815 | 108
2013-04-02 15:39:33 | 161 | 4815
...
I'm trying to determine how many session I had, as well as average session duration per user on a given day. A session ends when an article was not read within 30 minutes after another article.
Question
How can I efficiently determine how many session I had on a given day? I'm using PHP and MySQL.
My first idea is to query all that data for a given day, sorted by user. Then I iterate through each user, check if an impression was within 30 minutes of the last impression, and tally up a total count of session each user had that day.
Since we have around 2 million impressions a day on our site, I'm trying to optimize this report generator.
Try this query
Query 1:
select
#sessionId:=if(#prevUser=user_id AND diff <= 1800 , #sessionId, #sessionId+1) as sessionId,
#prevUser:=user_id AS user_id,
article_id,
date,
diff
from
(select #sessionId:=0, #prevUser:=0) b
join
(select
TIME_TO_SEC(if(#prevU=user_id, TIMEDIFF(date, #prevD), '00:00')) as diff,
#prevU:=user_id as user_id,
#prevD:=date as date,
article_id
from
tbl
join
(select #prev:=0, #prevU=0)a
order by
user_id,
date) a
[Results]:
| SESSIONID | USER_ID | ARTICLE_ID | DATE | DIFF |
-----------------------------------------------------------------
| 1 | 161 | 4815 | 2013-04-02 15:39:33 | 0 |
| 2 | 815 | 2342 | 2013-04-02 15:33:23 | 0 |
| 2 | 815 | 108 | 2013-04-02 15:38:21 | 298 |
| 3 | 815 | 108 | 2013-04-02 16:38:21 | 3600 |
This query will return a unique session for every new user and also for same user if the next article read is after 30 mins as per your requirement mentioned in your question. The diff column returns the seconds difference between the 2 articles by the same user which helps us count the sessionId. Now using this result it will be easy for you to count the average time per user and also total time per session.
Hope this helps you...
SQL Fiddle
If the concept of the user "session" is important to your analytics, then I would start logging data in your table to make querying of session-related data not such a painful process. A simple approach would be to log your PHP session ID. If your PHP session id is set to have the same 30 minute expiry, and you log the PHP session ID to this table then you would basically have exactly what you are looking for.
Of course that won't help you with your existing records. I would probably go ahead and create the session field and then back-populate it with randomly generated "session" id's. I wouldn't look for a fully SQL solution for this, as it may not do what you want in terms of handling edge cases (sessions spanning across days, etc.). I would write a script to perform this backfill, which would contain all the logic you need.
My general approach would be to SELECT all the records like this:
SELECT user_id, date /* plus any other fields like unique id that you would need for insert */
FROM Article_Impressions
WHERE session_id IS NULL
ORDER BY user_id ASC, date ASC
Note: make sure you have index on both user_id and date fields.
I would then loop through the result set, building a temp array of each user_id, and loop through that array for all date values assigning a randomly generated session id which would change each time the date change was greater than 30 minutes. Once the user value increments, I would make inserts for that previous user to update the session_id values and then reset the temp array to empty and continue that process with the next user.
Note that it is probably important to take the approach of keeping a relatively small temp/working array like this, as with the number of records you are talking about, you are likely not going to be able to read the entire result set into an array in memory.
Once your data is populated, the query becomes trivial:
Unique sessions for each day:
SELECT DATE(date) as `day`, COUNT(DISTINCT session_id) AS `unique_sessions`
FROM Article_Impressions
GROUP BY `day`
ORDER BY `day` DESC /* or ASC depending on how you want to view it */
Average sessions per day:
SELECT AVG(sessions_per_day.`unique_sessions`) AS `average_sessions_per_day`
FROM
(
SELECT DATE(date) as `day`, COUNT(DISTINCT session_id) AS `unique_sessions`
FROM Article_Impressions
GROUP BY `day`
) AS sessions_per_day
GROUP BY sessions_per_day.`day`
Note: you need an index on the new session_id field.
I'm going to make a Mysql based calendar system where you can have repeating pattern for lets say every monday forever and ever. It must also cover static/once-only events. What I'm wondering about, is which solution would be most logical (and best) for me to use. I have four methods which I'm wondering to chose between.
Method #1
Make a function which accepts parameters from and to. This function would create a temporary table table which imports existing static schedule through INSERT ... SELECT. Afterward it would read of the pattern table and populate the temporary table through the peroid based on from and to.
This solution seems nice from the point of view that queries will be simplier to fetch data with and it works into infinity since you can just repopulate the table depending of which month you're loading. What I'm curious about is whenever this might be a laggy way to do it or not.
Method #2
Create and join given patterns through a subquery and JOIN with static calendar.
This seems to be rather annoying since the queries would be a lot more bigger and would probably not be good at all(?).
Method #3
Basicly just INSERT pattern for lets say one year ahead. Then I guess a cron job would repopulate to make it one year ahead always.
This is a simple way to do it, but it feels like a lot of unneeded data stored and it doesn't really give the infinity which I'm after.
Method #4 (Suggested by Veger)
If I understand correctly, this method would fetch the pattern from another query and creates events upon execution. It's similar to my thoughts regarding Method #1 in that way that I consider simple pattern to create several rows.
However if this would be implemented outside Mysql, I would loose some database functionality which I'm after.
I hope you guys understood my situation, and if you could suggest either given and argue why it's the best or give another solution.
Personally I like the Method #1 the most, but I'm curious if it's laggy to repopulate the calendar table each and every call.
I have built this kind of calendar before. I found the best way to do it is to approach it the way that crons are scheduled. So in the database, make a field for minute, hour, day of month, month, and day of week.
For an event every Friday in June and August at 10:00pm your entry would look like
Minute Hour DayOfMonth Month DayOfWeek
0 22 * 6,8 5
You could then have a field that flags it as a one time event which will ignore this information and just use the start date and duration. For events that repeat that end eventually (say every weekend for 3 months) you just need to add an end date field.
This will allow you to select it back easily and reduce the amount of data that needs to be stored. It simplifies your queries as well.
I don't think there is a need to create temporary tables. To select back the relevant events you would select them by the calendar view. If your calendar view is by the month, your select would look something like:
SELECT Events.*
FROM Events
WHERE (Month LIKE '%,'.$current_month.',%' OR Month = '*')
AND DATE(StartDate) >= "'.date('Y-m-d', $firstDayOfCurrentMonth).'"
AND DATE(EndDate) <= "'.date('Y-m-d', $lastDayOfCurrentMonth).'"
Obviously this should be in a prepared statement. It also assumes that you have a comma before and after the first and last value in the comma separated list of months (ie. ,2,4,6,). You could also create a Month table and a join table between the two if you would like. The rest can be parsed out by php when rendering your calendar.
If you show a weekly view of your calendar you could select in this way:
SELECT Events.*
FROM Events
WHERE (DayOfMonth IN ('.implode(',', $days_this_week).','*')
AND (Month LIKE '%,'.$current_month.',%' OR Month = '*'))
AND DATE(StartDate) >= "'.date('Y-m-d', $firstDayOfCurrentMonth).'"
AND DATE(EndDate) <= "'.date('Y-m-d', $lastDayOfCurrentMonth).'"
I haven't tested those queries so there maybe some messed up brackets or something. But that would be the general idea.
So you could either run a select for each day that you are displaying or you could select back everything for the view (month, week, etc) and loop over the events for each day.
I like Veger's solution best .. instead of populating multiple rows you can just populate the pattern. I suggest the crontab format .. it works so well anyway.
You can query all patterns for a given customer when they load the calendar and fill in events based on the pattern. Unless you have like thousands of patterns for a single user this should not be all that slow. It should also be faster than storing a large number of row events for long periods. You will have to select all patterns at once and do some preprocessing but once again, how many patterns do you expect per user? Even 1000 or so should be pretty fast.
I've had this idea since I was still programming in GW Basic ;-) though, back then, I took option #3 and that was it. Looking back at it, and also some of the other responses, this would be my current solution.
table structure
start (datetime)
stop (datetime, nullable)
interval_unit ([hour, day, week, month, year?])
interval_every (1 = every <unit>, 2 every two <units>, etc.)
type ([positive (default), negative]) - will explain later
Optional fields:
title
duration
The type field determines how the event is treated:
positive; normal treatment, it shows up in the calendar
negative; this event cancels out another (e.g. every Monday but not on the 14th)
helper query
This query will narrow down the events to show:
SELECT * FROM `events`
WHERE `start` >= :start AND (`stop` IS NULL OR `stop` < :stop)
Assuming you query a range by dates alone (no time component), the the value of :stop should be one day ahead of your range.
Now for the various events you wish to handle.
single event
start = '2012-06-15 09:00:00'
stop = '2012-06-15 09:00:00'
type = 'positive'
Event occurs once on 2012-06-15 at 9am
bounded repeating event
start = '2012-06-15 05:00:00'
interval_unit = 'day'
interval_every = 1
stop = '2012-06-22 05:00:00'
type = 'positive'
Events occur every day at 5am, starting on 2012-06-15; last event is on the 22nd
unbounded repeating event
start = '2012-06-15 13:00:00'
interval_unit = 'week'
interval_every = 2
stop = null
type = 'positive'
Events occur every two weeks at 1pm, starting on 2012-06-15
repeating event with exceptions
start = '2012-06-15 16:00:00'
interval_unit = 'week'
interval_every = 1
type = 'positive'
stop = null
start = '2012-06-22 16:00:00'
type = 'negative'
stop = '2012-06-22 16:00:00'
Events occur every week at 4pm, starting on 2012-06-22; but not on the 22nd
I would suggest something around the lines of this:
Split your Events table into 2 because there are clearly 2 different types recurring events and static events and depending on the type they will have different attributes.
Then for a given Event look-up you would run 2 queries, one against each Event Type. For the static events table you would defiantly need (at least) one datetime field so the lookup for a given month would simply use that feild in the conditions (where event_date > FirstDayOfTheMonth and event_date < LastDayOfTheMonth ). Same logic for a weekly/yearly view.
This result set would be combined with a second result set from the recurring events table. Possible attributes could be similar to crontab entries, using day of week/day of month as the 2 main variables. If you're looking at a monthly view,
select * from recurring_events where DayOfWeek in (1,2,3,4,5,6,7) or (DayOfMonth > 0 and DayOfMonth < #NumberOfDaysInThisMonth )
Again similar if for a weekly/yearly view. To make this even simpler to interface, use stored procedures with all the logic for determining 'which days of the week are found between date A and date B'.
Once you have both result sets, you could aggregate them together in the client then display them together. The adavantage to this is there will be no need for "mock/empty records" nor async cronjobs which pre-fill, the queries could easily happen on the fly and if performance actually degrades, add a caching layer, especially for a system of this nature a cache makes perfect sense.
I'm actually looking for something similar to this and my solution so far (on paper I didn't start to structure or code yet) stores in 2 tables:
the "events" would get the date of the first occurrence, the title and description (plus the auto-increment ID).
the "events_recursion" table would cross reference to the previous table (with an event_id field for instance) and could work in 2 possible ways:
2.A: store all the occurrences by date (i.e. one entry for every occurrence so 4 if yo want to save "every friday of this month" or 12 for "the 1st of every month in 2012")
2.B: or saving the interval (I would save it in seconds) from the date of the first event in a field + the date of the last occurrence (or end of recursion) in another field such as
ID: 2
EVENT_ID: 1
INTERVAL: 604800 (a week if I'm not mistaken)
END: 1356912000 (should be the end of this year)
Then when you open the php that shows the schedule it would check for the event still active in that month with a joint between the two tables.
The reason why I would use 2 tables cross-referenced instead of saving all in one tables just comes from the facts that my projects sees very crazy events such as "every fridays AND the 3rd monday of every month" (that in this case would be 1 entry in the events tables and 2 with same "event_id" field in the second table. BTW my projects is for music teachers that here got small work on strict schedules decided 3 or 6 months at a time and are a real mess).
But as I have said i haven't started yet so I'm looking forward to seeing your solution.
PS: please forgive (and forget) my english, first isn't my language and second it is pretty late night and I'm sleepy
Maybe check out some great ideas from MySQL Events
and some more:
http://phpmaster.com/working-with-mysql-events/?utm_source=rss&utm_medium=rss&utm_campaign=phpmaster-working-with-mysql-events
http://dev.mysql.com/doc/refman/5.5/en/create-event.html
the best solution depends on whether you want to favor standard compliance (RFC5545) or working exclusively within MySQL.
depend on on flexible your recurrence rule engine needs to be. If you want simple rules (every 1st of month or every January, ...) then the solutions offered above have been detailed at length.
However should you want your application to offer compatibility with existing standards (RFC5545) which involves much more complex rules you should have a look at this SO post when building a calendar app, should i store dates or recurrence rules in my database?
I would do it as I explained here. It will create an infinite calander:
PHP/MySQL: Model repeating events in a database but query for date ranges
The downside is that there will be some calculation during the query. If you need a high performance website, preloading the data will be the way to go. You dont even have to preload all the events in the calendar, to make it possible for easy changing the values in a single event. But it would be wise to store all dates from now till ....
Now using cached values does make it less infinite, but it will increase speed.
Copy of awnser for easy access:
I would create a tally table with just one col called id and fill that table with numbers from 0 to 500. Now we easily use that to make selections instead of using a while loop.
Id
-------------------------------------
0
1
2
etc...
Then i'd store the events in a table with Name as varchar, startdate as datetime and repeats as int
Name | StartDate | Repeats
-------------------------------------
Meeting | 2012-12-10 00:00:00 | 7
Lunch | 2012-12-10 00:00:00 | 1
Now we can use the tally table to select all dates between two dates by using:
SELECT DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY) as showdate
FROM `tally`
WHERE (DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY)<='2012-12-20 00:00:00')
ORDER BY Id ASC
ShowDate
-------------------------------------
2012-12-09 00:00:00
2012-12-10 00:00:00
2012-12-11 00:00:00
2012-12-12 00:00:00
2012-12-13 00:00:00
2012-12-14 00:00:00
2012-12-15 00:00:00
2012-12-16 00:00:00
2012-12-17 00:00:00
2012-12-18 00:00:00
2012-12-19 00:00:00
2012-12-20 00:00:00
Then we join this on the events table to calculate the difference between the startdate and the showdate. We devided the results of this by the repeats column and if the remainder is 0, we have match.
All combined becomes:
SELECT E.Id, E.Name, E.StartDate, E.Repeats, A.ShowDate, DATEDIFF(E.StartDate, A.ShowDate) AS diff
FROM events AS E, (
SELECT DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY) as showdate
FROM `tally`
WHERE (DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY)<='2012-12-20 00:00:00')
ORDER BY Id ASC
) a
WHERE MOD(DATEDIFF(E.StartDate, A.ShowDate), E.Repeats)=0
AND A.ShowDate>=E.StartDate
Which results in
Id | Name |StartDate | Repeats | ShowDate | diff
---------------------------------------------------------------------------------
1 | Meeting | 2012-12-10 00:00:00 | 7 | 2012-12-10 00:00:00 | 0
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-10 00:00:00 | 0
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-11 00:00:00 | -1
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-12 00:00:00 | -2
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-13 00:00:00 | -3
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-14 00:00:00 | -4
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-15 00:00:00 | -5
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-16 00:00:00 | -6
1 | Meeting | 2012-12-10 00:00:00 | 7 | 2012-12-17 00:00:00 | -7
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-17 00:00:00 | -7
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-18 00:00:00 | -8
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-19 00:00:00 | -9
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-20 00:00:00 | -10
Now you could (and should!) speed things up. For instance by directly storing dates in a table so you can just select all dates directly instead of using a tally table with dateadd. Every thing you can cache and dont have to calculate again is good.