DBMS дар ин бобат аз рӯи ҳамон принсипҳо кор мекунанд - "Ба ман гуфтанд, ки кобед, ман мекобанд". Дархости шумо метавонад на танҳо равандҳои ҳамсояро суст карда, пайваста захираҳои протсессориро суст кунад, балки тамоми пойгоҳи додаҳоро "партофт" ва тамоми хотираи дастрасро "мехӯрад". муҳофизат аз рекурсияҳои беохир - масъулияти худи таҳиякунанда.
Дар PostgreSQL, қобилияти истифодаи дархостҳои рекурсивӣ тавассути WITH RECURSIVE
Дархостҳои рекурсивӣ нанависед
Ва навъҳои рекурсивиро нависед. Бо эхтиром, К.О.
Дар асл, PostgreSQL бисёр функсияҳоро таъмин мекунад, ки шумо метавонед онҳоро истифода баред не рекурсияро татбиқ кунед.
Муносибати куллан дигарро ба масъала истифода баред
Баъзан шумо метавонед ба мушкилот аз "тарафи дигар" нигоҳ кунед. Ман дар макола чунин вазъиятро мисол овардам
WITH RECURSIVE src AS (
SELECT '{2,3,5,7,11,13,17,19}'::integer[] arr
)
, T(i, val) AS (
SELECT
1::bigint
, 1
UNION ALL
SELECT
i + 1
, val * arr[i]
FROM
T
, src
WHERE
i <= array_length(arr, 1)
)
SELECT
val
FROM
T
ORDER BY -- отбор финального результата
i DESC
LIMIT 1;
Ин дархостро метавон бо варианти коршиносони математика иваз кард:
WITH src AS (
SELECT unnest('{2,3,5,7,11,13,17,19}'::integer[]) prime
)
SELECT
exp(sum(ln(prime)))::integer val
FROM
src;
Ба ҷои ҳалқаҳо generator_series-ро истифода баред
Фарз мекунем, ки мо бо вазифаи тавлиди ҳама префиксҳои имконпазир барои сатр рӯ ба рӯ мешавем 'abcdefgh'
:
WITH RECURSIVE T AS (
SELECT 'abcdefgh' str
UNION ALL
SELECT
substr(str, 1, length(str) - 1)
FROM
T
WHERE
length(str) > 1
)
TABLE T;
Шумо мутмаин ҳастед, ки дар ин ҷо рекурсия лозим аст?.. Агар шумо истифода баред LATERAL
и generate_series
, он гоҳ ба шумо ҳатто ба CTE ниёз нахоҳед дошт:
SELECT
substr(str, 1, ln) str
FROM
(VALUES('abcdefgh')) T(str)
, LATERAL(
SELECT generate_series(length(str), 1, -1) ln
) X;
Тағир додани сохтори пойгоҳи додаҳо
Масалан, шумо ҷадвали паёмҳои форумро доред, ки бо пайвастшавӣ аз кӣ ба кӣ посух додааст ё ришта дар
CREATE TABLE message(
message_id
uuid
PRIMARY KEY
, reply_to
uuid
REFERENCES message
, body
text
);
CREATE INDEX ON message(reply_to);
Хуб, дархости маъмулӣ барои зеркашии ҳама паёмҳо дар як мавзӯъ чунин аст:
WITH RECURSIVE T AS (
SELECT
*
FROM
message
WHERE
message_id = $1
UNION ALL
SELECT
m.*
FROM
T
JOIN
message m
ON m.reply_to = T.message_id
)
TABLE T;
Аммо азбаски мо ҳамеша ба тамоми мавзӯъ аз паёми реша ниёз дорем, пас чаро мо не ID-и худро ба ҳар як вуруд илова кунед автомат?
-- добавим поле с общим идентификатором темы и индекс на него
ALTER TABLE message
ADD COLUMN theme_id uuid;
CREATE INDEX ON message(theme_id);
-- инициализируем идентификатор темы в триггере при вставке
CREATE OR REPLACE FUNCTION ins() RETURNS TRIGGER AS $$
BEGIN
NEW.theme_id = CASE
WHEN NEW.reply_to IS NULL THEN NEW.message_id -- берем из стартового события
ELSE ( -- или из сообщения, на которое отвечаем
SELECT
theme_id
FROM
message
WHERE
message_id = NEW.reply_to
)
END;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER ins BEFORE INSERT
ON message
FOR EACH ROW
EXECUTE PROCEDURE ins();
Акнун тамоми дархости рекурсивии моро метавон танҳо ба ин кам кард:
SELECT
*
FROM
message
WHERE
theme_id = $1;
Истифода бурдани "маҳдудкунандаҳо"
Агар мо бо ягон сабаб сохтори пойгоҳи додаҳоро тағир дода натавонем, биёед бубинем, ки мо ба чӣ такя карда метавонем, то ҳатто мавҷудияти хато дар маълумот ба рекурсияи беохир оварда нарасонад.
Ҳисобкунаки амиқи рекурсия
Мо танҳо дар ҳар як қадами рекурсия ҳисобкунакро як маротиба зиёд мекунем, то он даме, ки мо ба ҳадде расидем, ки онро баръало нокифоя мешуморем:
WITH RECURSIVE T AS (
SELECT
0 i
...
UNION ALL
SELECT
i + 1
...
WHERE
T.i < 64 -- предел
)
Pro: Вақте ки мо кӯшиш мекунем, ки гардиш кунем, мо то ҳол на бештар аз маҳдудияти муқарраршудаи такрори "дар амиқ" иҷро мекунем.
Омӯз: Ҳеҷ кафолате нест, ки мо дубора ҳамон сабтро коркард нахоҳем кард - масалан, дар чуқурии 15 ва 25 ва баъд ҳар як +10. Ва касе дар бораи «фано» чизе ваъда надод.
Ба таври расмӣ чунин рекурсия беохир нахоҳад буд, аммо агар дар ҳар як қадам шумораи сабтҳо ба таври экспоненсиалӣ зиёд шавад, мо ҳама хуб медонем, ки он чӣ гуна анҷом меёбад...
Посбони "роҳ"
Мо бо навбат ҳамаи идентификаторҳои объектро, ки дар роҳи рекурсия дучор шудаем, ба массив илова мекунем, ки он "роҳ"-и беназири он аст:
WITH RECURSIVE T AS (
SELECT
ARRAY[id] path
...
UNION ALL
SELECT
path || id
...
WHERE
id <> ALL(T.path) -- не совпадает ни с одним из
)
Pro: Агар дар маълумот давра вуҷуд дошта бошад, мо комилан як сабтро дар як роҳ такроран коркард намекунем.
Омӯз: Аммо дар айни замон мо метавонем ба таври айнан ҳамаи сабтҳоро бидуни такрори худ канор гузорем.
Маҳдудияти дарозии роҳ
Барои роҳ надодан ба ҳолати «саргардонии» рекурсия дар умқи нофаҳмо, мо метавонем ду усули қаблиро якҷоя кунем. Ё, агар мо нахоҳем, ки майдонҳои нолозимро дастгирӣ кунем, шарти идомаи рекурсияро бо арзёбии дарозии роҳ илова кунед:
WITH RECURSIVE T AS (
SELECT
ARRAY[id] path
...
UNION ALL
SELECT
path || id
...
WHERE
id <> ALL(T.path) AND
array_length(T.path, 1) < 10
)
Усули мувофиқи табъи худ интихоб кунед!
Манбаъ: will.com