Разложение запросов PostgreSQL - PullRequest
       12

Разложение запросов PostgreSQL

2 голосов
/ 12 августа 2011

Мне не удается разложить простые запросы SQL.Я использую PostgreSQL, но мой вопрос также связан с другими СУБД.

Рассмотрим следующий пример.У нас есть столы заказов, и мы хотим найти первый заказ, после которого общая сумма превысила некоторый лимит:

drop table if exists orders cascade;

/**
Table with clients' orders
*/
create table orders(
date timestamp,
amount integer
/**
Other columns omitted
*/
);

/**
Populate with test data
*/
insert into orders(date,amount) 
values
('2011-01-01',50),
('2011-01-02',49),
('2011-01-03',2),
('2011-01-04',1000);

/**
Selects first order that caused exceeding of limit 
*/
create view first_limit_exceed
as
select min(date) from
(
    select  o1.date
    from orders o1,
         orders o2
    where o2.date<=o1.date
    group by o1.date
    having sum(o2.amount) > 100
) limit_exceed;

/**
returns "2011-01-03 00:00:00"
*/
select * from first_limit_exceed;

Теперь давайте усложним задачуПредположим, мы хотим найти общую сумму только для строк, которые удовлетворяют некоторому предикату.У нас много таких предикатов, и создание отдельной версии view first_limit_exceed было бы ужасным дублированием кода.Поэтому нам нужен какой-то способ создания параметризованного представления и передачи либо отфильтрованного набора строк, либо самого предиката.В Postgres мы можем использовать функции языка запросов в качестве параметризованных представлений.Но Postgres не позволяет функции получать в качестве аргумента ни набор строк, ни другую функцию.Я все еще могу использовать интерполяцию строк на стороне клиента или в функции plpgsql, но она подвержена ошибкам и трудна для тестирования и отладки.Любой совет?

Ответы [ 2 ]

2 голосов
/ 12 августа 2011

В PostgreSQL 8.4 и позже:

SELECT  *
FROM    (
        SELECT  *,
                SUM(amount) OVER (ORDER BY date) AS psum
        FROM    orders
        ) q
WHERE   psum > 100
ORDER BY
        date
LIMIT 1

Добавьте любые предикаты, которые вы хотите во внутренний запрос:

SELECT  *
FROM    (
        SELECT  *,
                SUM(amount) OVER (ORDER BY date) AS psum
        FROM    orders
        WHERE   date >= '2011-01-03'
        ) q
WHERE   psum > 100
ORDER BY
        date
LIMIT 1
0 голосов
/ 12 августа 2011

Звучит так, будто вы пытаетесь поместить слишком много кода в базу данных. Если вас интересуют строки определенного отношения, которые удовлетворяют определенному предикату, просто выполните оператор select с соответствующим предложением where в клиентском коде. Иметь представления, которые принимают предикаты в качестве параметров, - это заново изобретать колесо, которое sql уже хорошо решает.

С другой стороны, я вижу аргумент для хранения самих запросов в базе данных, чтобы они могли быть объединены в более крупные отчеты. Эти два еще лучше обрабатываются кодом приложения. Я мог бы подойти к такой проблеме, используя библиотеку, которая хороша для динамической генерации sql (например, sqlalchemy), и затем сохраняя представления запросов (объекты выражений sqlalchemy 'pickleable') в виде BLOB-объектов в базе данных.

Другими словами, базы данных являются представителями фактов . Вы храните в них знания. приложения должны действовать по запросам пользователей . Когда вы обнаруживаете, что определяете преобразования для данных, на самом деле это скорее вопрос ожидания и реализации запросов реальных пользователей, а не просто точного сохранения знаний.

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

...