Как избежать временных таблиц?

Имеется некий запрос.
В нём сначала находятся зависимости
Например ChildID и ParentID.
Сначала мы ищем строки ParentID=NULL и затем выбираем строки где ParentID в результате выборки. Этот запрос используется в основном запросе в IN в WHERE.
Проблема в том, что это должно работать быстро и надёжно, а при большом количестве записей производительность сильно падает. Можно ли избежать временных таблиц в такой конфигурации запроса?

Опции просмотра комментариев

Выберите предпочитаемый вами способ показа комментариев и нажмите "Сохранить настройки" для активации изменений.

Очень путанное объяснение.

Очень путанное объяснение. Давайте на примере, а?

Ну давайте на реальном

Ну давайте на реальном попробуем, сильно упрощённом для сути.
В таблице forum хранятся записи, которые нужно использовать в окончательной выборке. Сначала выбираются форумы без родителей, а потом по их Id выбираются потомки первой линии. Таблицы activeaccess используются для флага доступа. Жирным выделено то, что не нравится.

CREATE OR REPLACE FUNCTION MYLIST(
i_userid integer,
i_categoryid integer,
i_parentid integer)
RETURNS SETOF MYLIST_return_type AS
$BODY$DECLARE
_rec MYLIST_return_type%ROWTYPE;
BEGIN

CREATE TEMPORARY TABLE flrtbl1(userid integer, forumid integer, parentid integer, readaccess integer) ON COMMIT DROP;
CREATE TEMPORARY TABLE flrtbl2(userid integer,
forumid integer, parentid integer, readaccess integer) ON COMMIT DROP;

INSERT INTO flrtbl1(userid, forumid, parentid,readaccess)
SELECT
i_userid,
b.forumid AS ForumID,
b.parentid,
0 as readaccess
FROM
category a
JOIN forum b on b.categoryid=a.categoryid
JOIN activeaccess x on x.forumid=b.forumid
WHERE
x.readaccess IS NOT FALSE AND
(i_CategoryID IS NULL OR a.categoryid=i_categoryid) AND
((i_ParentID IS NULL AND b.parentid IS NULL)
OR b.parentid=i_parentid)
AND
x.userid = i_userid
ORDER BY
a.sortorder,
b.sortorder;

INSERT INTO flrtbl2(userid, forumid, parentid, readaccess)
SELECT
i_userid,
b.forumid,
b.parentid,
0 as readaccess
FROM
category a
JOIN forum b on b.categoryid=a.categoryid
JOIN activeaccess x on x.forumid=b.forumid
WHERE
((b.flags & 2)=0 OR x.readaccess IS NOT FALSE ) AND
(i_CategoryID IS NULL OR a.categoryid=i_categoryid) AND
(b.ParentID IN (SELECT forumid FROM flrtbl1))
AND
x.userid = i_userid
ORDER BY
a.sortorder,
b.sortorder;
insert into flrtbl2(userid, forumid, parentid, readaccess)
select * FROM flrtbl1;
-- more childrens can be added to display as a tree

FOR _rec IN
SELECT
a.categoryid,
a.name AS Category,
b.forumid AS ForumID,
b.parentid,
b.name AS Forum,
x.readaccess::integer
FROM
category a
JOIN forum b on b.categoryid=a.categoryid
JOIN activeaccess x on x.forumid=b.forumid
WHERE
(i_categoryid IS NULL OR a.categoryid=i_categoryid) AND
x.userid = i_userid and
(b.forumid IN (SELECT forumid FROM flrtbl2) )
ORDER BY
a.sortorder,
b.sortorder
LOOP
RETURN NEXT _rec;
END LOOP;

END;

$BODY$
LANGUAGE 'plpgsql' VOLATILE SECURITY DEFINER
COST 100
ROWS 1000;

Вместо flrtbl1можно

Вместо flrtbl1можно использовать VIEW
В остальных случаях могут помочь функции, который используют рекурсию:
http://postgresql.ru.net/manual/queries-with.html

Насчёт VIEW - неплохая идея,

Насчёт VIEW - неплохая идея, по-моему они в 9 хранятся на диске и в данном случае это неплохо. Спс попробую.
WITH тут не поможет, так как каждый раз будет производится оценка подзапроса.
Почему Postgre е имеет простейших вещей типа табличных переменных, которые в MSSQL server c 2000 присутствуют? (((
Неужели в IN нельзя запихнуть ничего кроме SELECT и перечисления?

Если вам нужны переменные,

Если вам нужны переменные, пишите функцию на любом процедурном языке программирования, который вы знаете и который поддерживается PostgreSQL - их немало. Более того, в 9-ке есть возможность писать на процедурном языке не создавая функцию. Не знаю что такое "табличная переменная", но на всякий случай гляньте сюда, может найдёте аналог:
http://postgresql.ru.net/manual/extend.html

VIEW на диске никогда не хранится, ибо VIEW - это не таблица, это правила для её построения. Каждый раз когда вы обращаетесь к VIEW выполняется запрос, который этот VIEW строит. (кэширование конечно работает, но я сейчас не про это).

VIEW даже индексируется в

VIEW даже индексируется в некотрых БД.
Оно хранится на диске например в MS SQL Server и если я не ошибаюсь в Oracle. И даже индексируется. Когда у вас используются вычисляемые колонки в виде бинарных флагов, это важно.
Firebird текущей версии использует внутреннее хранение VIEW по типу хранимых процедур с кэшированием. Это современный подход. Если у вас выборка из таблицы с сотнями тысяч записей, то например в MySQL - это вообще бесполезно.
В результате долгих размышлений и учитывая что на C мне на хостинге добавить ничего не дадут я напишу отдельную функцию, возвращающую таблицу вместо 2х временных. Если будет гарантировано, что это выполнятся в одной транзакции то проблем не будет.

Опции просмотра комментариев

Выберите предпочитаемый вами способ показа комментариев и нажмите "Сохранить настройки" для активации изменений.

Back to top

(С) Виктор Вислобоков, 2008-2023