Фильтрация по результату оконной функции в Postgresql - PullRequest
21 голосов
/ 18 февраля 2011

Хорошо, изначально это была просто шутка с моим другом, но это превратилось в интересный технический вопрос:)

У меня есть следующая таблица stuff:

CREATE TABLE stuff
(
    id serial PRIMARY KEY,
    volume integer NOT NULL DEFAULT 0,
    priority smallint NOT NULL DEFAULT 0,
);

Таблица содержит записи для всех моих вещей, с соответствующим объемом и приоритетом (насколько мне это нужно).

У меня есть сумка с указанным объемом, скажем 1000. Я хочу выбрать из таблицы все вещи, которые я могу положить в сумку, сначала собрав самые важные вещи.

Это похоже на случай использования оконных функций, поэтому вот запрос, который я придумал:

select s.*, sum(volume) OVER previous_rows as total
 from stuff s
 where total < 1000
 WINDOW previous_rows as
  (ORDER BY priority desc ROWS between UNBOUNDED PRECEDING and CURRENT ROW)
 order by priority desc

Однако проблема в том, что Постгрес жалуется:

ERROR:  column "total" does not exist
LINE 3:  where total < 1000

Если я уберу этот фильтр, общий столбец будет правильно рассчитан, результаты правильно отсортированы, но все материал будет выбран, что не то, что я хочу.

Итак, как мне это сделать? Как выбрать только те вещи, которые могут поместиться в сумку?

Ответы [ 2 ]

14 голосов
/ 18 февраля 2011

Я не знаю, квалифицируется ли это как «более изящное», но оно написано не так, как решение Cybernate (хотя оно по сути то же самое)

WITH window_table AS 
( 
   SELECT s.*, 
          sum(volume) OVER previous_rows as total
   FROM stuff s
   WINDOW previous_rows as 
        (ORDER BY priority desc ROWS between UNBOUNDED PRECEDING and CURRENT ROW)
) 
SELECT * 
FROM window_table
WHERE total < 1000
ORDER BY priority DESC 

Если под «более элегантным» вы подразумеваете что-то, что избегает подвыбора, то ответ «нет»

14 голосов
/ 18 февраля 2011

Я не работал с PostgreSQL.Тем не менее, я бы предпочел использовать встроенное представление.

SELECT a.*
FROM (
    SELECT s.*, sum(volume) OVER previous_rows AS total
    FROM stuff AS s
    WINDOW previous_rows AS (
         ORDER BY priority desc
         ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    )
    ORDER BY priority DESC
) AS a
WHERE a.total < 1000;
...