Sql запрос для расчета цены номера - PullRequest
4 голосов
/ 27 апреля 2011

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

room | people | price   | hotel
 1   |    1   |   200   |   A
 2   |    2   |   99    |   A
 3   |    3   |   95    |   A
 4   |    1   |   90    |   B
 5   |    6   |   300   |   B

Я ищу самую низкую цену вданный отель для x количество людей

на 1, я бы ожидал, что у меня будет:

hotel | price
  A   |  200
  B   |  90

на 2, я бы:

hotel | price
  A   |  99

это потому, что отельВ нет номеров, которые могут вместить 2 человека.6 не могут быть использованы для менее (или более), чем 6 человек.

для отеля. Цена 99, это потому, что я использую номер 2

, для результата 6 должно быть:

hotel | price
  A   |  394
  B   |  300

, поэтому для отеля A я беру комнаты 1,2, 3 и для отеля B самая низкая цена была бы за одну комнату 5 за 300

Я сделал это с ограничением, что я смогу разместить людей максимум в 3 комнатах, и это приемлемо, но мой запрос медленный:( это выглядит примерно так:

select a.hotel,a.price+a1.price+a2.price 
from room_price a, room_price a1, room_price a2 
where 
    a.room<> a1.room
and a1.room<> a2.room
and a.room<> a2.room
and a.hotel = a1.hotel
and a.hotel = a2.hotel

после этого я сделал груп по отелю и взял min (цену), и это сработало ... но выполняя 3 раза запрос, который возвращает мне room_price и чем декартово произведениеЭто заняло много времени. В room_price содержится около 5000 элементов, и это довольно сложный sql, который генерирует эти данные (принимает даты, начало и конец нескольких цен, обмен валюты ...)

Я могу использовать sql,пользовательские функции ... или что-нибудь, что сделает эту работу быстрой, но я бы предпочел остаться на уровне базы данных без необходимости обрабатывать эти данные в приложении (я использую Java), так как я буду распространять это далее на рекламуd Дополнительные данные к запросу.

Буду признателен за любую помощь.

Ответы [ 3 ]

3 голосов
/ 27 апреля 2011

Сам запрос:

WITH RECURSIVE
setup as (
    SELECT 3::INT4 as people
),  
room_sets AS (
    SELECT
        n.hotel,
        array[ n.room ] as rooms,
        n.price,
        n.people
    FROM
        setup s,
        room_price n
    WHERE
        n.people <= s.people
    UNION ALL
    SELECT
        rs.hotel,
        rs.rooms || n.room,
        rs.price + n.price as price,
        rs.people + n.people as people
    FROM
        setup s,
        room_sets rs
        join room_price n using (hotel)
    WHERE
        n.room > rs.rooms[ array_upper( rs.rooms, 1 )]
        AND rs.people + n.people <= s.people
),
results AS (
    SELECT
        DISTINCT ON (rs.hotel)
        rs.*
    FROM
        room_sets rs,
        setup s
    WHERE
        rs.people = s.people
    ORDER BY
        rs.hotel, rs.price
)   
SELECT * FROM results;

Протестировано на этом наборе данных:

CREATE TABLE room_price (
    room INT4 NOT NULL,
    people INT4 NOT NULL,
    price INT4 NOT NULL,
    hotel TEXT NOT NULL,
    PRIMARY KEY (hotel, room)
);  
copy room_price FROM stdin WITH DELIMITER ',';
1,1,200,A
2,2,99,A
3,3,95,A
4,1,90,B
5,6,300,B
\.

Обратите внимание, что это станет намного медленнее, когда вы добавите больше комнат на свою базу.

Ах, чтобы настроить, для скольких людей вы хотите получить результаты - измените настройку.

Написал подробное объяснение о том, как это работает.

2 голосов
/ 27 апреля 2011

Похоже, что ваш запрос неверен с предложением FROM ... похоже, псевдонимы не в порядке

from room_price a, room_price,a1 room_price,room_price a2 

и должно быть

from room_price a, room_price a1, room_price a2 

То, что МОЖЕТ давать запросу ложный псевдоним / дополнительную таблицу, давая какой-то декартово произведение, заставляющее его зависать ...

--- хорошо в предложении FROM ...

Кроме того, и просто мысль ... Так как "Комната" представляется внутренним столбцом идентификатора с автоматическим приращением, она никогда не будет дублироваться, например Комната 100 в отеле A и Комната 100 в отеле B. Ваш запрос делать <> в комнате имеет смысл, так что вы никогда не будете сравнивать по всем направлениям на всех 3 столах ...

Почему бы не заставить объединения a1 и a2 претендовать только на комнату БОЛЬШЕ, чем комната "a". В противном случае вы будете повторно тестировать одни и те же условия снова и снова. Исходя из данных вашего примера, как раз в отеле A, у вас есть номера комнат 1, 2 и 3. Таким образом, вы сравниваете

a     a1    a2
1     2     3
1     3     2
2     1     3
2     3     1
3     1     2
3     2     1

Поможет ли сравнение только там, где "a1" всегда больше, чем "a", а "a2" всегда больше, чем "a1", таким образом, выполняя тесты

a   a1   a2
1   2    3 

даст те же результаты, что и все остальные, и, таким образом, увеличит ваш результат до одной записи в этом случае ... но затем, как вы можете действительно сравнить с местоположением только ДВУХ типов номеров "hotel B". Вы НИКОГДА не получите ответ, так как ваша квалификация для номеров

a <> a1 AND
a <> a2 AND
a1 <> a2

Возможно, вы захотите попытаться сократить только одно самостоятельное соединение для a1, a2 и оставить сравнение только для двух, например

select a1.hotel, a1.price + a2.price
   from room_price a1, room_price a2
   where a1.hotel = a2.hotel
     and a2.room > a1.room


For hotel "A", you would thus have final result comparisons of 
a1   a2
1    2
1    3
2    3

and for hotel "B"
a1   a2
4    5
1 голос
/ 27 апреля 2011

Реализация <> будет иметь довольно большое влияние, когда вы начнете рассматривать большие наборы данных. Особенно, если предварительная фильтрация не уменьшает его размер. Используя это, вы можете потенциально отрицать возможность оптимизации прямого запроса и реализации индексирования, но также представление может не реализовывать индексирование, потому что SQL попытается запустить фильтры для запроса и представления для таблиц в как можно меньшем числе операторов ( в ожидании оптимизации сделано двигателем).

В идеале я бы начал с просмотра и подтвердил, что он правильно оптимизирован. Просто глядя на сам запрос, у вас больше шансов быть оптимизированным;

SELECT
    a.hotel, a.price + a1.price + a2.price  
FROM    
    room_price a, 
    room_price, 
    room_price a1,
    room_price a2  
WHERE   
    (a.room > a1.room OR a.room < a1.room) AND 
    (a1.room > a2.room OR a1.room < a2.room) AND 
    (a.room > a2.room OR a.room < a2.room) AND 
    a.hotel = a1.hotel AND 
    a.hotel = a2.hotel 

Кажется, он возвращает те же результаты, но я не уверен, как вы реализуете этот запрос в своем общем решении. Так что рассмотрите только природу изменений в существующем запросе и то, что вы уже сделали.

Надеюсь, это поможет. Если нет, вам, возможно, придется подумать, что делает представление и как оно работает, представление, которое возвращает результаты из временной таблицы или переменной, также не может реализовать индексацию. В этом случае, возможно, вам лучше будет создать индексированную временную таблицу.

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