views:

115

answers:

4

hello,

In my application users should be able to define dates when they are available. Ie. user joe may define he's available:
- at every monday, wednesday and sunday between 15:00 and 17:00 from 1.09.2009 to 15.11.2009. - at 2.09.2009 between 12:00 and 14:00 and so on...

Dates may be defined maximum 1 year in future.

Users may add, edit and delete defined dates.

Other users may search available users ie. - find all users available 8.09.2009 at 15:30

The problem is how to design sql tables, which allow creating and editing cyclic dates and efficient searching.

I'm using postgresql but rather looking for any guidelines. Maybe someone has experience with similar problem?

Thanks in advance

A: 

You will want to store a users availability in the database and use a programming language to check the availability rather than having any complicated logic in the database itself. Using an ORM tool would make this easier.

A users availability would consist of a list of datetime pairs.

Nosrama
There will be milions of records. I'm afraid doing it at application level will be too slow.
mlomnicki
+1  A: 

Option 1

You would store availability with 3 columns (assuming a single id col to identify a user)

availability_start, availability_end, user_id

searching based on a datetime would then be something like

select
  user_id
from
  availability
where
  desired_datetime > availability_start
and
  desired_datetime < availability_end

you may want to remove past dates depending on the functionality you want to offer. Obviously you'll need to decide how far into the future to store dates based on peoples cyclic date definitions.

Option 2

If it is always as simple as choosing individual days of the week and times for those days. You could store cyclic dates as

user_id, day_of_week, availability_time_start, availability_time_end

The start and end times need no date component to them. Searching based on a day of the week and a time would be something like

select
  user_id
from
  availability
where
  desired_day_of_week = day_of_week
and 
  desired_time > availability_time_start
and
  desired_time < availability_time_end


As an aside, there are libraries which assist with creating such recurring date patterns, where I work we use this (Java), there may well be RFC 2445 implementations in a language suitable to you.

If you use something like that then you won't be storing actual dates, but just the details of the recurrence patterns which won't really help you with your problem. We store these details by pretty simply taking the values from the recurrence definition and persisting them to the db one field to one column.

You could then also store the dates/times for some defined amount of time in the future and recalculate these on any changes to the recurrence definitions, this could obviously become quite a large operation depending on how many schedules people have and how far in the future you want to store data.

Robin
I've added describtion about maximum date. As far as I understand RFC2445 may be used only for export calendar. I can't imagine how to store icalendar format in db and do searching on it. I like simple ideas, but I think it's too simple. What about storing and editing cyclic dates?
mlomnicki
I've added option 2 which may be of help and a brief mention of how we simply persist RFC 2445 definitions
Robin
+1  A: 

When I've dealt with data like this before, I've taken a "rules" approach similar to this to accomodate by-day scheduling and block scheduling:

CREATE TABLE availability_rules (
  id SERIAL UNIQUE,
  user_id NOT NULL REFERENCES users(id),
  day_of_week INTEGER CONSTRAINT valid_day_of_week CHECK (day_of_week BETWEEN 0 and 6)
  start_time TIME,
  start_date DATE,
  end_time TIME,
  end_date DATE
);

Note: You would need to add additional constraints to the rules table to make sure that end_date + end_time >= start_date + start_time, etc.

Then, you can create an "availability" view to generate a normalized schedule with user_id, date, start_time, and end_time. Using 8.4, this is easy with the new generate_series datetime capabilities. (http://www.postgresql.org/docs/current/static/functions-srf.html) It is easy enough to write a function that does the same thing if you're on 8.3 or earlier.

Dave Pirotte
constrainsts are definitely good idea. My current approach uses generate_series but I'm afraid that for milion of record it will be inefficient, indices won't be used.
mlomnicki
I think you'll be fine. But, for the sake of maximum performance... you could materialize the view into a cached availability table and, rather than updating the whole thing all at once, use triggers to selectively insert or update based on what rule was changed and how. Then, you can query against a materialized view to get availability while still maintaining the rules separately.Before going that route, I'd at least run an EXPLAIN ANALYZE against the view to measure performance.
Dave Pirotte
that's definitely best proposal. However I found nothing about 'cached availability table'. How to handle it in postgres? Just give me a link or any basic guidelines, please.
mlomnicki
There are lots of different approaches, but you should do some reading on materialized views (http://blog.revsys.com/2006/01/when_to_use_a_m.html) which is what I meant when I said "cached table". You can either regenerate the materialized view table after every new availability_rule entry (probably inefficient) or write a trigger function to selectively update the materialized view table after new/updated/deleted entries. The idea is to denormalize for speed. Can you give me some more details about your application and most common read/write scenarios?
Dave Pirotte
This is application for planning and performing online lessons. Teachers define their cyclic or non-cyclic free time. Time definitions may overlap. Pupils reserve teachers' time so gaps appear. Reservation can be cyclic also. Users must be able to check if there is teacher available at let's say 5.09 16:00. I've done it with 3 tables: 'definitions' table where users define dates (ie. every monday from 1.09.2009 to 20.12.2009 between 3pm to 7pm, expanded definitions table where cycles are expanded to single dates, and real_free_times table with free_times minus reserved times.
mlomnicki
A: 

What you could do is have a table that has the following design:

Availability
  - userId, Foreign key to user table
  - startDate, datetime
  - endDate, datetime
  - startTime, datetime
  - endTime, datetime
  - monday, bit
  - tuesday, bit
  - wednesday, bit
  - thursday, bit
  - friday, bit
  - saturday, bit
  - sunday, bit

Now you can check for availability at certain weekdays with the following (MSSQL yntax)

SELECT * FROM User u WHERE EXISTS(
    SELECT 1 FROM Availability WHERE userId = u.id AND
    @startDate >= startDate AND @endDate <= endDate AND
    @startTime >= startTime AND @endTime <= endTime AND
    (monday = 1 OR tuesday = 1)
)

Which would give you all users that are available between @startDate and @endDate at mondays or tuesdays between @startTime and @endTime.

Runeborg
that is very interesting idea, I'll check it right now
mlomnicki