Хранение открытых часов / праздников - PullRequest
1 голос
/ 17 декабря 2010

Какую схему SQL вы бы предложили для хранения часов и праздников, и какой запрос проверить, открыт ли ресторан? Прямо сейчас у меня есть это:

CREATE TABLE hours (
    "restaurant" integer NOT NULL REFERENCES restaurants ON DELETE CASCADE,
    "dow" integer NOT NULL,
    "open" time NOT NULL,
    "close" time NOT NULL
);
CREATE FUNCTION is_open(r integer) RETURNS boolean AS $$
    DECLARE
        h record;
        t time;
    BEGIN
        SELECT open, close INTO h
        FROM hours WHERE restaurant = r AND dow = EXTRACT(dow FROM now());
        IF NOT FOUND THEN
            RETURN false;
        END IF;
        t := current_time;
        IF h.close <= h.open THEN
            RETURN (t < h.close OR t > h.open);
        ELSE
            RETURN (t > h.open AND t < h.close); 
        END IF;
    END;
$$ LANGUAGE plpgsql;

Но на самом деле это не работает, потому что, например, ресторан может быть открыт до 2:00 утра, после чего мне нужно будет проверить предыдущий dow.

Чтобы все было немного сложнее, я должен иметь дело с праздниками:

CREATE TABLE holidays (
    "restaurant" integer NOT NULL REFERENCES restauraunts ON DELETE CASCADE,
    "day" date NOT NULL
);

Что имеет ту же проблему - если ресторан открыт с 15:30 до 2:00, это означает, что они также закрыты для блока с полуночи до двух.

Мне не удалось обернуть голову, чтобы найти чистое, элегантное решение для этого (несколько грязных приходили и уходили). Мне нужно пойти погулять и вернуться к нему - тем временем я решил, что я позволю вам, ребята, покончить с этим.

Ответы [ 3 ]

1 голос
/ 18 декабря 2010

Кажется, что это неправильно, отвечать на мой собственный вопрос, но я нашел что-то, что, кажется, работает, как бы грязно это ни было:

CREATE FUNCTION is_open(r integer) RETURNS boolean AS $$
    DECLARE
        t time;
        yesterday date;
        dow_today integer;
        dow_yesterday integer;
    BEGIN
        t := current_time;
        yesterday := current_date - 1;
        dow_today := EXTRACT(dow FROM current_date);
        dow_yesterday := EXTRACT(dow FROM yesterday);
        PERFORM * FROM hours
        WHERE restaurant = r AND ((
            dow = dow_today
            AND NOT EXISTS(
                SELECT * FROM holidays 
                    WHERE restaurant = r AND day = current_date
            ) AND (
                (open < close AND t > open AND t < close)
                OR (open >= close AND t > open)
            )
        ) OR (
            open >= close AND dow = dow_yesterday
            AND NOT EXISTS(
                SELECT * FROM holidays 
                WHERE restaurant = r AND day = yesterday
            ) AND t < close
        ));   
        RETURN FOUND;
    END;
$$ LANGUAGE plpgsql;
0 голосов
/ 18 декабря 2010

В дизайне вашего стола отсутствует информация, которая становится очевидной, когда вы пытаетесь понять, что означает закрытие и открытие. Например, 12 к 2. Означает ли это 2 часа или 14? Не делая предположений, нет способа решить это.

Используйте интервалы:

Например, если ваш ресторан работает с 13:30 до 01:30, можно проверить, находится ли сейчас () между:

test=# select  
  now() > (now()::date || ' 13:30')::timestamp 
  and now() < (now()::date || ' 13:30')::timestamp + interval '12 hours';

 ?column?
----------
 t
(1 row)

Таким образом, важен только начальный индекс Доу, и у вас не должно быть проблем с переносом.

0 голосов
/ 18 декабря 2010

Подводя итог комментариям:

1 - используйте общую структуру запроса из этого вопроса.

2 - добавьте битовый флаг в вашу таблицу для ClosePastMidnight или OpenPastMidnight (в зависимости от того, что лучше всего подходит для вашего мышления), который показывает, что закрытие наступает в следующий календарный день, и соответственно измените свою логику.

...