PostgreSQL выполняет несколько динамических условий WHERE без динамической записи SQL

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

Кроме того, эти числа находятся в диапазонах внутри каждого сегмента.

Пользователь может указать желаемый диапазон значений любого количества произвольных записей. Возвращаются записи, в которых перекрытие для ВСЕХ указанных параметров (сегментов).

Вы заметите, что есть минимум и максимум. Это диапазон. Увидев, перекрываются ли они, я могу получить результаты значительно быстрее, чем при использовании запроса диапазона. Это метод оптимизации.

Вот пример с двумя условиями:

SELECT  id
FROM    mytable2
WHERE   (val_low && (ARRAY(SELECT generate_series((0 * 64) + 20, (0 * 64) + 28))) OR
        val_high && (ARRAY(SELECT generate_series((0 * 64) + 20, (0 * 64) + 28))))
AND     (val_low && (ARRAY(SELECT generate_series((1 * 64) + 12, (1 * 64) + 15))) OR 
        val_high && (ARRAY(SELECT generate_series((1 * 64) + 12, (1 * 64) + 15))))

Сегменты val_low и val_high проверяются на пересечение с массивом указанных диапазонов.

Проблема в том, что мне нужно динамически построить этот запрос в функции. Список параметров передается функции (как определенный пользователем тип [массив]), запрос динамически генерируется, а затем выполняется.

Это работает, но я хочу сделать это без необходимости писать SQL в функции.

В частности, функции будет передан массив пользовательского типа следующим образом:

param_num int,
val_low   int,
val_high  int

Значения в вызове функции generate_series равны (param_num * 64) + val_low, (param_num * 64) + val_high.

Это возможно?

Пример создания данных:

DROP TABLE IF EXISTS
        mytable2;

CREATE TABLE
        mytable2
        (
                id          INT NOT NULL PRIMARY KEY,
                val_low     int[],
                val_high    int[]
        );

SELECT  SETSEED(0.20130725);

WITH    t AS
        (
        SELECT  id,
                1 + FLOOR(RANDOM() * 24) AS l1, (RANDOM() * 8)::int AS h1,
                1 + FLOOR(RANDOM() * 24) AS l2, (RANDOM() * 8)::int AS h2,
                1 + FLOOR(RANDOM() * 24) AS l3, (RANDOM() * 8)::int AS h3,
                1 + FLOOR(RANDOM() * 24) AS l4, (RANDOM() * 8)::int AS h4
        FROM    generate_series(1, 500000) id
        )
INSERT
INTO    mytable2
SELECT  T.id, array[t.l1, (1 * 64) + t.l2, (2 * 64) + t.l3, (3 * 64) + t.l4], 
        array[t.l1 + t.h1, (1 * 64) + t.l2 + t.h2, (2 * 64) + t.l3 + t.h3, 
        (3 * 64) + t.l4 + t.h4]
FROM    T;

CREATE INDEX
    ix_mytable2_vhstore_low
ON      mytable2
USING   GIN (val_low);


CREATE INDEX
    ix_mytable2_vhstore_high
ON      mytable2
USING   GIN (val_high);

Пример запроса:

--EXPLAIN ANALYZE
SELECT COUNT(1)
FROM
(
    SELECT  id
    FROM    mytable2
    WHERE   (val_low && (ARRAY(SELECT generate_series(20, 28))) OR val_high &&
                (ARRAY(SELECT generate_series(20, 28))))
        AND (val_low && (ARRAY(SELECT generate_series((1 * 64) + 12, (1 * 64) + 15)))
                OR val_high && (ARRAY(SELECT generate_series((1 * 64) + 12, (1 * 64) + 15))))
) m;

Результаты: 54983


person IamIC    schedule 27.07.2013    source источник
comment
Я не вижу используемых значений таблицы. Это означает, что будут возвращены либо все строки, либо ни одна. Верный?   -  person Clodoaldo Neto    schedule 27.07.2013
comment
Возвращаются все строки, в которых либо val_low, либо val_high перекрывают ВСЕ предоставленные серии.   -  person IamIC    schedule 27.07.2013


Ответы (1)


скрипт SQL

with s as (
    select array(select generate_series(
            a[i][1] * 64 + a[i][2], a[i][1] * 64 + a[i][3]
        )) as a
    from
        (values (array[[0,20,28],[1,12,15]])) s(a)
        cross join
        generate_series(1, array_length(array[[0,20,28],[1,12,15]], 1)) g(i)
)
select id
from mytable2 cross join s
group by id
having count((not(val_low && a or val_high && a)) or null) = 0

array[[0,20,28],[1,12,15]] - переданный параметр

person Clodoaldo Neto    schedule 27.07.2013
comment
Спасибо за это. Мне придется поиграть с ним некоторое время, так как он возвращает больше записей, чем должен. - person IamIC; 27.07.2013
comment
Исходные данные являются типом диапазона. - person IamIC; 27.07.2013
comment
рандом посеян, поэтому результаты будут одинаковыми. Но вы можете использовать пример запроса, чтобы увидеть, какими должны быть результаты, и сравнить их с вашей новой функцией. - person IamIC; 27.07.2013
comment
Setseed делает его псевдослучайным, поэтому мы оба получим один и тот же набор. Но это не проблема. Вы можете сравнить результаты двух запросов, и если они одинаковы, ваш работает. - person IamIC; 27.07.2013
comment
Удалите [1,12,15] часть массива (оставив только первый сегмент), и вы увидите 0 результатов вместо 187168. Кроме того, на странице скрипки ваш источник был int, а не int[], поэтому я не уверен, что будет обрабатывать то же самое. - person IamIC; 27.07.2013
comment
Кроме того, я добавил еще один параметр (просто скопировал № 2), и результаты изменились. - person IamIC; 27.07.2013
comment
@IanC Я исправил ошибку количества параметров и использовал ваш код создания в новой скрипке . Но ограничил количество строк до 500. - person Clodoaldo Neto; 27.07.2013
comment
@IanC В этой скрипке я разместил подсчеты для моего и вашего запроса. - person Clodoaldo Neto; 27.07.2013