Предыдущая тема :: Следующая тема |
Автор |
Сообщение |
MVZ Участник со стажем
Вступление в Клуб: 14.03.2023
|
Пн Апр 10, 2023 08:51  Помощь в рефакторинге |
|
Полезность: Нет оценки
|
Коллеги добрый день,
прошу вашей помощи в рефакторинге. Была поставлена задача реализовать некоторый сервис в АБС. Естественно как и многое подобное я нашел в ЦФТ аналог, довольно изящно реализованный, который к сожалению я не смог повторить в ввиду отсутствия опыта. Тем не менее задачу свою я решил, но теперь хочу сделать рефакторинг и приблизить свою реализацию к ЦФТ.
Ниде код ЦФТ который я бы хотел реализовать но не совсем понимаю
Код: |
for (with recursive rc(id, parent , cls , nocyc) as (
select x(x , x.[PARENT] , x%class , ','||x||',') in ::[ADDRESS_OBJ] where x = p_obj
union all
select x(x , x.[PARENT] , x%class , rr.nocyc||x||',') in ::[ADDRESS_OBJ]
join (rc : rr) on rr.parent = x and rr.cls <> p_class and instr(rr.nocyc, ','||x||',') = 0)
select r(r.id : obj) in rc where r.cls = p_class)
loop
return r.obj;
end loop;
|
а вот моя реализация
Код: |
SELECT cl( cl : id,
cl.[NAME] : C_NAME,
cl.[DOCS].[DOC_TYPE].[NAME] : C_DOC_TYPE,
cl.[DOCS].[CREATED] : C_DOC_DATE,
addr%class : C_C,
addr_str.[NAME] : C_STR_NAME,
addr_dst%id : C_DISTRICT_ID,
addr_dst.[NAME] : C_DISTRICT_NAME,
addr_cty.[NAME] : C_CITY_NAME,
addr_reg.[OFF_NAME] : C_REGION_NAME,
addr_reg.[NAME] : C_ICWINNER,
(select u(u%id) in ::[PATT_SIGNS] all where u%collection = cl.[DOCS].[DATA] fetch 1) : C_REF_FILE_DATA
) IN ::[CL_PRIV]
LEFT JOIN ( ::[PERSONAL_ADDRESS] ALL : addr) ON addr%collection = cl.[ADDRESSES] AND addr.[TYPE] = ::[ADDRESS_TYPE](KOD = 'REGISTRATION')
LEFT JOIN(::ADDRESS_OBJ ALL : addr_str ) ON addr.[ADDR_OBJ] = addr_str%id
LEFT JOIN(::ADDRESS_OBJ ALL : addr_cty ) ON addr_str.[PARENT_M] = addr_cty%id
LEFT JOIN(::ADDRESS_OBJ ALL : addr_dst ) ON addr_cty.[PARENT_M] = addr_dst%id
LEFT JOIN(::ADDRESS_OBJ ALL : addr_reg ) ON addr_dst.[PARENT_M] = addr_reg%id AND addr_reg.[OFF_NAME] = '[удалено]'
WHERE cl.[DOCS].[DOC_TYPE] = ::[TYPE_GRAPHIC](CODE = 'ITR_ZACH_SOTS')
AND cl.[DOCS].[CREATED] >= V_DATE_BEG
AND cl.[DOCS].[CREATED] <= V_DATE_END
ORDER BY addr_dst.[NAME], cl.[NAME] ;
|
Что я хотел бы исправить:
Как видно из примера изящность кода ЦФТ заключается в применении рекурсии которая и более декларативна и проста, однако в то же время и сложна для таких как я, так как в отличии от моей реализации вся конечная реализация перекладывается на компилятор.
Помогите пожалуйста понять что за оператор
nocyc и во что он трансформируется компилятором
что за хитрая конструкция rr.nocyc||x||',' понимаю что это и есть ключ рекурсии но опять же не понимаю во что это транслируется компилятором, и каким образом последующий join с union делает магию с рекурсией
Очень хочется понять как работает рекурсия.
Еще один момент
подскажите пожалуйста как обратиться к переменной формы из процедуры описанной в секции локальные описания. оператор this там не виден да и сами переменные формы так же не видны |
|
 |
-Eugene- Участник
Вступление в Клуб: 21.04.2008
|
Пн Апр 10, 2023 16:05   |
|
Полезность: 1
|
1. По кнопке F12 можно посмотреть во что раскрывается данный pl/plus код. Далее, можно скопировать его в pl/sql developer и там поэкспериментировать...
Но, по мне, ваш код гораздо проще будет понять и сопровождать в дальнейшем, чем это "изящное решение".
2. В локальную процедуру можно передать this как параметр.
Что-то вроде: procedure myproc(p_this ref [MY_TYPE]) is begin ... end;
И вызов: myproc(this);
3. если нужна проверка попадания даты в диапазон, то обычно пишут так: V_DATE_BEG <= cl.[DOCS].[CREATED] and cl.[DOCS].[CREATED] < V_DATE_END+1.
Это позволяет избежать ошибки, если cl.[DOCS].[CREATED] содержит время, хотя дата попадает в диапазон.
В вашем случае условие не сработает, если V_DATE_END = 10.04.2023 00:00:00, а cl.[DOCS].[CREATED] = 10.04.2023 11:15:26 |
|
 |
Эмиралька Эксперт
Вступление в Клуб: 09.11.2015
|
Вт Апр 11, 2023 19:05  Re: Помощь в рефакторинге |
|
Полезность: 1
|
MVZ пишет: | Коллеги добрый день,
прошу вашей помощи в рефакторинге. Была поставлена задача реализовать некоторый сервис в АБС. Естественно как и многое подобное я нашел в ЦФТ аналог, довольно изящно реализованный, который к сожалению я не смог повторить в ввиду отсутствия опыта. Тем не менее задачу свою я решил, но теперь хочу сделать рефакторинг и приблизить свою реализацию к ЦФТ.
Ниде код ЦФТ который я бы хотел реализовать но не совсем понимаю
Код: |
for (with recursive rc(id, parent , cls , nocyc) as (
select x(x , x.[PARENT] , x%class , ','||x||',') in ::[ADDRESS_OBJ] where x = p_obj
union all
select x(x , x.[PARENT] , x%class , rr.nocyc||x||',') in ::[ADDRESS_OBJ]
join (rc : rr) on rr.parent = x and rr.cls <> p_class and instr(rr.nocyc, ','||x||',') = 0)
select r(r.id : obj) in rc where r.cls = p_class)
loop
return r.obj;
end loop;
|
а вот моя реализация
Код: |
SELECT cl( cl : id,
cl.[NAME] : C_NAME,
cl.[DOCS].[DOC_TYPE].[NAME] : C_DOC_TYPE,
cl.[DOCS].[CREATED] : C_DOC_DATE,
addr%class : C_C,
addr_str.[NAME] : C_STR_NAME,
addr_dst%id : C_DISTRICT_ID,
addr_dst.[NAME] : C_DISTRICT_NAME,
addr_cty.[NAME] : C_CITY_NAME,
addr_reg.[OFF_NAME] : C_REGION_NAME,
addr_reg.[NAME] : C_ICWINNER,
(select u(u%id) in ::[PATT_SIGNS] all where u%collection = cl.[DOCS].[DATA] fetch 1) : C_REF_FILE_DATA
) IN ::[CL_PRIV]
LEFT JOIN ( ::[PERSONAL_ADDRESS] ALL : addr) ON addr%collection = cl.[ADDRESSES] AND addr.[TYPE] = ::[ADDRESS_TYPE](KOD = 'REGISTRATION')
LEFT JOIN(::ADDRESS_OBJ ALL : addr_str ) ON addr.[ADDR_OBJ] = addr_str%id
LEFT JOIN(::ADDRESS_OBJ ALL : addr_cty ) ON addr_str.[PARENT_M] = addr_cty%id
LEFT JOIN(::ADDRESS_OBJ ALL : addr_dst ) ON addr_cty.[PARENT_M] = addr_dst%id
LEFT JOIN(::ADDRESS_OBJ ALL : addr_reg ) ON addr_dst.[PARENT_M] = addr_reg%id AND addr_reg.[OFF_NAME] = '[удалено]'
WHERE cl.[DOCS].[DOC_TYPE] = ::[TYPE_GRAPHIC](CODE = 'ITR_ZACH_SOTS')
AND cl.[DOCS].[CREATED] >= V_DATE_BEG
AND cl.[DOCS].[CREATED] <= V_DATE_END
ORDER BY addr_dst.[NAME], cl.[NAME] ;
|
Что я хотел бы исправить:
Как видно из примера изящность кода ЦФТ заключается в применении рекурсии которая и более декларативна и проста, однако в то же время и сложна для таких как я, так как в отличии от моей реализации вся конечная реализация перекладывается на компилятор.
Помогите пожалуйста понять что за оператор
nocyc и во что он трансформируется компилятором
что за хитрая конструкция rr.nocyc||x||',' понимаю что это и есть ключ рекурсии но опять же не понимаю во что это транслируется компилятором, и каким образом последующий join с union делает магию с рекурсией
Очень хочется понять как работает рекурсия.
|
nocyc - это алиас запроса. переименуйте в rr.krokodil - получите тот же самый код.
Вам надо курить рекурсивные запросы, но я бы предложила начать с иерархических, они как-то попроще, кмк.
"хитрая" конструкция rr.nocyc||x||',' - это рекурсивная реализация иерархической функции SYS_CONNECT_BY_PATH().
Секрет рекурсивных запросов в следующем: надо разбирать запрос итеративно, как его выполняет машина.
Итерация 1: выполняется верхний запрос, он является базовым, уже к нему лепится запрос со второй итерации и других.
В иерархическом запросе аналогом является условие из start with.
Затем рассматриваем вторую итерацию. На ней уже доступен результат первой итерации, обращение к ней выполняется через алиас rc, хотя я бы назвала prior_, чтобы было понятнее.
Запрос пишется после union all в рекурсивном запросе. В иерархическом соединение выполняется с помощью connect by.
Затем третья итерация. В этом случае опять используется запрос из второй части with после union all, но на третьей итерации запросу доступны уже данные второй итерации. Стоит подумать над условием прерывания этого ухождения вглубь иерархии.
И, наконец, в иерархическом запросе есть условие where, которое накладывается на результат запроса уже после того, как все данные иерархии получены и выстроены. В рекурсивном запросе - это запрос за блоком with.
В рекурсивном запросе требуется сразу обозначить все поля, которые участвуют в построении иерархии в виде перечисления внутри слова with.
Вроде всё описала...
Могу на примере ещё проиллюстрировать. надо? |
|
 |
Эмиралька Эксперт
Вступление в Клуб: 09.11.2015
|
Вт Апр 11, 2023 19:22  Re: Помощь в рефакторинге |
|
Полезность: 1
|
MVZ пишет: | Коллеги добрый день,
Еще один момент
подскажите пожалуйста как обратиться к переменной формы из процедуры описанной в секции локальные описания. оператор this там не виден да и сами переменные формы так же не видны |
Вы, наверное, имеете в виду параметры операции? так как переменные операции в локальных описаниях вполне доступны. Переменные операции по сути своей - глобальные переменные операции.
Параметры формы следует передавать в функции и процедуры локальных описаний как параметры этих функций и процедур. Далее, this - не оператор, это предопределённый параметр операции (есть разница между списочной и простой операциями), доступен в секции валидации и теле операции, содержит значение id из представления, в котором вызвана операция. Работает аналогично любому другому параметру операции.
Также возможно, вы имеете в виду именно контролы экранной формы, тут вам нужно понимать, что экранная форма является визуальным отображением переменных и параметров операции, доступ к которым разработчик предоставил оператору. Всё, что будет заполнено оператором в контроле, будет записано в соответствующую связанную с контролом переменную или параметр.
Как управлять возникновением событий для валидации введённых значений описывать долго, посмотрите в документации? |
|
 |
Volod Эксперт
Вступление в Клуб: 19.09.2007
|
Ср Апр 12, 2023 10:15   |
|
Полезность: 1
|
Наверное проще будет понять, выполнив такой запрос
Код: | select * from (
with /*___RECURSIVE___*/
RC(ID,PARENT,CLS,NOCYC) as (
select b1.ID A$1, b1.C_PARENT A$2, b1.CLASS_ID A$3, ','||b1.ID||',' A$4
from Z#ADDRESS_OBJ b1
where b1.ID = (select id from Z#ADDRESS_OBJ g1 where g1.C_OBJECTID = 817264 ) --P_OBJ
union all
select c1.ID A$1, c1.C_PARENT A$2, c1.CLASS_ID A$3, d1.NOCYC||c1.ID||',' A$4
from Z#ADDRESS_OBJ c1 join RC d1 on d1.PARENT = c1.ID
and d1.CLS <> 'CODE_REGION'-- P_CLASS
and INSTR(d1.NOCYC,','||c1.ID||',') = 0
)
select a1.ID OBJ,PARENT,CLS,NOCYC
from RC a1
-- where a1.CLS = 'CODE_REGION' --P_CLASS;
) |
Код: |
OBJ PARENT CLS NOCYC
-------------------------------------------------------
15023277 1921493 STREET ,15023277,
1921493 1918442 NAMES_CITY ,15023277,1921493,
1918442 CODE_REGION ,15023277,1921493,1918442, |
|
|
 |
MVZ Участник со стажем
Вступление в Клуб: 14.03.2023
|
Чт Апр 13, 2023 09:03   |
|
Полезность: Нет оценки
|
-Eugene- пишет: | 1. По кнопке F12 можно посмотреть во что раскрывается данный pl/plus код. Далее, можно скопировать его в pl/sql developer и там поэкспериментировать...
Но, по мне, ваш код гораздо проще будет понять и сопровождать в дальнейшем, чем это "изящное решение".
2. В локальную процедуру можно передать this как параметр.
Что-то вроде: procedure myproc(p_this ref [MY_TYPE]) is begin ... end;
И вызов: myproc(this);
3. если нужна проверка попадания даты в диапазон, то обычно пишут так: V_DATE_BEG <= cl.[DOCS].[CREATED] and cl.[DOCS].[CREATED] < V_DATE_END+1.
Это позволяет избежать ошибки, если cl.[DOCS].[CREATED] содержит время, хотя дата попадает в диапазон.
В вашем случае условие не сработает, если V_DATE_END = 10.04.2023 00:00:00, а cl.[DOCS].[CREATED] = 10.04.2023 11:15:26 |
Большое спасибо принял замечание |
|
 |
MVZ Участник со стажем
Вступление в Клуб: 14.03.2023
|
Чт Апр 13, 2023 09:09  Re: Помощь в рефакторинге |
|
Полезность: Нет оценки
|
Эмиралька пишет: | MVZ пишет: | Коллеги добрый день,
прошу вашей помощи в рефакторинге. Была поставлена задача реализовать некоторый сервис в АБС. Естественно как и многое подобное я нашел в ЦФТ аналог, довольно изящно реализованный, который к сожалению я не смог повторить в ввиду отсутствия опыта. Тем не менее задачу свою я решил, но теперь хочу сделать рефакторинг и приблизить свою реализацию к ЦФТ.
Ниде код ЦФТ который я бы хотел реализовать но не совсем понимаю
Код: |
for (with recursive rc(id, parent , cls , nocyc) as (
select x(x , x.[PARENT] , x%class , ','||x||',') in ::[ADDRESS_OBJ] where x = p_obj
union all
select x(x , x.[PARENT] , x%class , rr.nocyc||x||',') in ::[ADDRESS_OBJ]
join (rc : rr) on rr.parent = x and rr.cls <> p_class and instr(rr.nocyc, ','||x||',') = 0)
select r(r.id : obj) in rc where r.cls = p_class)
loop
return r.obj;
end loop;
|
а вот моя реализация
Код: |
SELECT cl( cl : id,
cl.[NAME] : C_NAME,
cl.[DOCS].[DOC_TYPE].[NAME] : C_DOC_TYPE,
cl.[DOCS].[CREATED] : C_DOC_DATE,
addr%class : C_C,
addr_str.[NAME] : C_STR_NAME,
addr_dst%id : C_DISTRICT_ID,
addr_dst.[NAME] : C_DISTRICT_NAME,
addr_cty.[NAME] : C_CITY_NAME,
addr_reg.[OFF_NAME] : C_REGION_NAME,
addr_reg.[NAME] : C_ICWINNER,
(select u(u%id) in ::[PATT_SIGNS] all where u%collection = cl.[DOCS].[DATA] fetch 1) : C_REF_FILE_DATA
) IN ::[CL_PRIV]
LEFT JOIN ( ::[PERSONAL_ADDRESS] ALL : addr) ON addr%collection = cl.[ADDRESSES] AND addr.[TYPE] = ::[ADDRESS_TYPE](KOD = 'REGISTRATION')
LEFT JOIN(::ADDRESS_OBJ ALL : addr_str ) ON addr.[ADDR_OBJ] = addr_str%id
LEFT JOIN(::ADDRESS_OBJ ALL : addr_cty ) ON addr_str.[PARENT_M] = addr_cty%id
LEFT JOIN(::ADDRESS_OBJ ALL : addr_dst ) ON addr_cty.[PARENT_M] = addr_dst%id
LEFT JOIN(::ADDRESS_OBJ ALL : addr_reg ) ON addr_dst.[PARENT_M] = addr_reg%id AND addr_reg.[OFF_NAME] = '[удалено]'
WHERE cl.[DOCS].[DOC_TYPE] = ::[TYPE_GRAPHIC](CODE = 'ITR_ZACH_SOTS')
AND cl.[DOCS].[CREATED] >= V_DATE_BEG
AND cl.[DOCS].[CREATED] <= V_DATE_END
ORDER BY addr_dst.[NAME], cl.[NAME] ;
|
Что я хотел бы исправить:
Как видно из примера изящность кода ЦФТ заключается в применении рекурсии которая и более декларативна и проста, однако в то же время и сложна для таких как я, так как в отличии от моей реализации вся конечная реализация перекладывается на компилятор.
Помогите пожалуйста понять что за оператор
nocyc и во что он трансформируется компилятором
что за хитрая конструкция rr.nocyc||x||',' понимаю что это и есть ключ рекурсии но опять же не понимаю во что это транслируется компилятором, и каким образом последующий join с union делает магию с рекурсией
Очень хочется понять как работает рекурсия.
|
nocyc - это алиас запроса. переименуйте в rr.krokodil - получите тот же самый код.
Вам надо курить рекурсивные запросы, но я бы предложила начать с иерархических, они как-то попроще, кмк.
"хитрая" конструкция rr.nocyc||x||',' - это рекурсивная реализация иерархической функции SYS_CONNECT_BY_PATH().
Секрет рекурсивных запросов в следующем: надо разбирать запрос итеративно, как его выполняет машина.
Итерация 1: выполняется верхний запрос, он является базовым, уже к нему лепится запрос со второй итерации и других.
В иерархическом запросе аналогом является условие из start with.
Затем рассматриваем вторую итерацию. На ней уже доступен результат первой итерации, обращение к ней выполняется через алиас rc, хотя я бы назвала prior_, чтобы было понятнее.
Запрос пишется после union all в рекурсивном запросе. В иерархическом соединение выполняется с помощью connect by.
Затем третья итерация. В этом случае опять используется запрос из второй части with после union all, но на третьей итерации запросу доступны уже данные второй итерации. Стоит подумать над условием прерывания этого ухождения вглубь иерархии.
И, наконец, в иерархическом запросе есть условие where, которое накладывается на результат запроса уже после того, как все данные иерархии получены и выстроены. В рекурсивном запросе - это запрос за блоком with.
В рекурсивном запросе требуется сразу обозначить все поля, которые участвуют в построении иерархии в виде перечисления внутри слова with.
Вроде всё описала...
Могу на примере ещё проиллюстрировать. надо? |
Эмиралька, спасибо вам большое за подробное описание и время, стало немного понятнее, но немного ... (скорее всего в силу моей природной тупости ) Да, если сможете дать пример для "особо-одаренных" я буду вам очень признателен. Я попытался покурить SYS_CONNECT_BY_PATH() но и тут провал. Дальше этого уйти не смог. Это попытка с наскока победить иерархическую таблицу, но если тямы нет то победить вряд-ли получится. Я вообще не понимаю чтьо происходит на уровне запроса для работы с иерархией.
Код: |
SELECT LPAD(' ', 2*level-1)||SYS_CONNECT_BY_PATH(с_name, '/') "Path"
FROM Z#ADDRESS_OBJ
START WITH с_name = 'Республика'
CONNECT BY PRIOR c_parent_m = id;
|
|
|
 |
MVZ Участник со стажем
Вступление в Клуб: 14.03.2023
|
Чт Апр 13, 2023 09:24   |
|
Полезность: Нет оценки
|
Volod пишет: | Наверное проще будет понять, выполнив такой запрос
Код: | select * from (
with /*___RECURSIVE___*/
RC(ID,PARENT,CLS,NOCYC) as (
select b1.ID A$1, b1.C_PARENT A$2, b1.CLASS_ID A$3, ','||b1.ID||',' A$4
from Z#ADDRESS_OBJ b1
where b1.ID = (select id from Z#ADDRESS_OBJ g1 where g1.C_OBJECTID = 817264 ) --P_OBJ
union all
select c1.ID A$1, c1.C_PARENT A$2, c1.CLASS_ID A$3, d1.NOCYC||c1.ID||',' A$4
from Z#ADDRESS_OBJ c1 join RC d1 on d1.PARENT = c1.ID
and d1.CLS <> 'CODE_REGION'-- P_CLASS
and INSTR(d1.NOCYC,','||c1.ID||',') = 0
)
select a1.ID OBJ,PARENT,CLS,NOCYC
from RC a1
-- where a1.CLS = 'CODE_REGION' --P_CLASS;
) |
Код: |
OBJ PARENT CLS NOCYC
-------------------------------------------------------
15023277 1921493 STREET ,15023277,
1921493 1918442 NAMES_CITY ,15023277,1921493,
1918442 CODE_REGION ,15023277,1921493,1918442, |
|
Вставил ваш код в SQL Developer, запустил - работает. Вспомнил как в детском саду к нам приходил фокусник, было непонятно что он делал но смотреть было безумно интересно - это лирика. Что касается уважения к вашему ответу и времени, я для себя попытался разложить код, что бы было немного понятнее его анализировать, получилось вот так
Код: |
select *
from (
with
RC( ID,
PARENT,
CLS,
NOCYC
) as ( select b1.ID A$1,
b1.C_PARENT A$2,
b1.CLASS_ID A$3,
','||b1.ID||',' A$4
from Z#ADDRESS_OBJ b1
where b1.ID = (select id from Z#ADDRESS_OBJ g1 where g1.C_OBJECTID = 817264 ) --P_OBJ
union all
select c1.ID A$1,
c1.C_PARENT A$2,
c1.CLASS_ID A$3,
d1.NOCYC||c1.ID||',' A$4
from Z#ADDRESS_OBJ c1
join RC d1 on d1.PARENT = c1.ID
and d1.CLS <> 'CODE_REGION'-- P_CLASS
and INSTR(d1.NOCYC,','||c1.ID||',') = 0
)
select a1.ID OBJ,
PARENT,
CLS,
NOCYC
from RC a1
-- where a1.CLS = 'CODE_REGION' --P_CLASS;
)
|
Теперь вопросы, касательно некоторых конструкций кода, которые я не понял
[list=]
[list] не пойму что это зачем и во что превращается Код: | d1.NOCYC||c1.ID||',' A$4 |
если я все правильно понял из комментария от Эмиральки, то
первая итерация это
Код: |
select b1.ID A$1,
b1.C_PARENT A$2,
b1.CLASS_ID A$3,
','||b1.ID||',' A$4
from Z#ADDRESS_OBJ b1
where b1.ID = (select id from Z#ADDRESS_OBJ g1 where g1.C_OBJECTID = 817264 ) --P_OBJ
|
пока пытался сформулировать вопрос понял что мозг закипел настолько что в принципе я ничего не понимаю насколько что даже вопрос задать не могу.
Простите дурака за то что отнимает ваше время. Пожалуйста объясните на пальцах (это просто крик отчаяния)
С Уважением,
Максим. |
|
 |
Эмиралька Эксперт
Вступление в Клуб: 09.11.2015
|
Чт Апр 13, 2023 17:32  Re: Помощь в рефакторинге |
|
Полезность: 2
|
MVZ пишет: |
Эмиралька, спасибо вам большое за подробное описание и время, стало немного понятнее, но немного ... (скорее всего в силу моей природной тупости ) Да, если сможете дать пример для "особо-одаренных" я буду вам очень признателен. Я попытался покурить SYS_CONNECT_BY_PATH() но и тут провал. Дальше этого уйти не смог. Это попытка с наскока победить иерархическую таблицу, но если тямы нет то победить вряд-ли получится. Я вообще не понимаю чтьо происходит на уровне запроса для работы с иерархией.
Код: |
SELECT LPAD(' ', 2*level-1)||SYS_CONNECT_BY_PATH(с_name, '/') "Path"
FROM Z#ADDRESS_OBJ
START WITH с_name = 'Республика'
CONNECT BY PRIOR c_parent_m = id;
|
|
В моей базе записей с именем "Республика" несколько, поэтому я выбрала только одну.
Вот иллюстрация вашего запроса, я его немного переписала:
Код: |
SELECT level, c_name, id, c_parent
, LPAD(' ', 4*level) || c_name
, LPAD(' ', 4*level) || id
, SYS_CONNECT_BY_PATH(c_name, ' -> ') "Path"
FROM Z#ADDRESS_OBJ
START WITH id = 3024091610 and c_name = 'Республика'
CONNECT BY PRIOR c_parent = id;
|
Найдите какую-нибудь запись с c_name = Республика, возьмите её id и подставьте в запрос вместо 3024091610.
Начинается всё с записи 3024091610. Она забирается в запрос на основании условия start with.
Затем вторая итерация. Здесь работает условие из строки connect by. По ключевому слову prior определяем, что из предыдущей записи нам нужна c_parent, выбрать те записи, у которых id совпадает с c_parent предыдущей записи. Другими словами, выбрать цепочку родителей, начиная с указанной дочерней.
Ну или я->мать->бабка->прабабка, до тех пор, пока известен предок (c_parent).
sys_connect_path при этом возвращает всю цепочку поиска. Запустите и посмотрите.
Но эта цепочка тривиальна, примитивна и неинтересна.
Рассмотрим обратную, когда нужно выбрать
я -> дочь 1 -> внучка 1 -> правнучка 1
-> правнучка 2
-> дочь 2 ->
и так далее, в другую сторону, в сторону потомков.
Код: |
SELECT level, c_name, id, c_parent
, LPAD(' ', 4*level) || c_name
, LPAD(' ', 4*level) || id
, SYS_CONNECT_BY_PATH(c_name, ' -> ') "Path"
FROM Z#ADDRESS_OBJ
START WITH c_parent is null and id = 1918442
CONNECT BY c_parent = PRIOR id;
|
Аналогично, найдите какую-нибудь запись с c_parent is null и начните строить дерево с неё.
Красиво, верно?
Когда будете разбирать запросы, посмотрите, с какой стороны стоит prior. prior - указывает на запись с предыдущей итерации.
Рекурсивный запрос отдельно разбирать уже не сегодня, у нас уже поздний вечер. |
|
 |
Эмиралька Эксперт
Вступление в Клуб: 09.11.2015
|
Чт Апр 13, 2023 17:48  Re: Помощь в рефакторинге |
|
Полезность: 2
|
Эмиралька пишет: | MVZ пишет: |
Эмиралька, спасибо вам большое за подробное описание и время, стало немного понятнее, но немного ... (скорее всего в силу моей природной тупости ) Да, если сможете дать пример для "особо-одаренных" я буду вам очень признателен. Я попытался покурить SYS_CONNECT_BY_PATH() но и тут провал. Дальше этого уйти не смог. Это попытка с наскока победить иерархическую таблицу, но если тямы нет то победить вряд-ли получится. Я вообще не понимаю чтьо происходит на уровне запроса для работы с иерархией.
Код: |
SELECT LPAD(' ', 2*level-1)||SYS_CONNECT_BY_PATH(с_name, '/') "Path"
FROM Z#ADDRESS_OBJ
START WITH с_name = 'Республика'
CONNECT BY PRIOR c_parent_m = id;
|
|
В моей базе записей с именем "Республика" несколько, поэтому я выбрала только одну.
Вот иллюстрация вашего запроса, я его немного переписала:
Код: |
SELECT level, c_name, id, c_parent
, LPAD(' ', 4*level) || c_name
, LPAD(' ', 4*level) || id
, SYS_CONNECT_BY_PATH(c_name, ' -> ') "Path"
FROM Z#ADDRESS_OBJ
START WITH id = 3024091610 and c_name = 'Республика'
CONNECT BY PRIOR c_parent = id;
|
Найдите какую-нибудь запись с c_name = Республика, возьмите её id и подставьте в запрос вместо 3024091610.
Начинается всё с записи 3024091610. Она забирается в результат на основании условия start with.
Затем вторая итерация. Здесь работает условие из строки connect by. По ключевому слову prior определяем, что с чем сравниваем.
Выбрать записи, у которых id совпадает с c_parent предыдущей записи. Другими словами, выбрать цепочку родителей, начиная с указанной дочерней.
Ну или я->мать->бабка->прабабка, до тех пор, пока известен предок (c_parent).
sys_connect_path при этом возвращает всю цепочку поиска. Запустите и посмотрите.
Но эта цепочка тривиальна, примитивна и неинтересна.
Рассмотрим обратную, когда нужно выбрать
я -> дочь 1 -> внучка 1 -> правнучка 1
-> правнучка 2
-> дочь 2 ->
и так далее, в другую сторону, в сторону потомков.
Код: |
SELECT level, c_name, id, c_parent
, LPAD(' ', 4*level) || c_name
, LPAD(' ', 4*level) || id
, SYS_CONNECT_BY_PATH(c_name, ' -> ') "Path"
FROM Z#ADDRESS_OBJ
START WITH c_parent is null and id = 1918442
CONNECT BY c_parent = PRIOR id;
|
Аналогично, найдите какую-нибудь запись с c_parent is null и начните строить дерево с неё.
Всё начинается с записи id = 1918442.
Она забирается в результат и на основании неё осуществляется поиск записей, которые ссылаются на неё полем c_parent, то есть дочерних, тех, для которых я являюсь родителем.
Затем для каждой найденной дочерней записи повторяем процесс.
Красиво, правда?
Когда будете разбирать запросы, посмотрите, с какой стороны стоит prior. prior - указывает на запись с предыдущей итерации.
Рекурсивный запрос отдельно разбирать уже не сегодня, у нас уже поздний вечер. |
|
|
 |
MVZ Участник со стажем
Вступление в Клуб: 14.03.2023
|
Пт Апр 14, 2023 06:02  Re: Помощь в рефакторинге |
|
Полезность: Нет оценки
|
Эмиралька пишет: | Эмиралька пишет: | MVZ пишет: |
Эмиралька, спасибо вам большое за подробное описание и время, стало немного понятнее, но немного ... (скорее всего в силу моей природной тупости ) Да, если сможете дать пример для "особо-одаренных" я буду вам очень признателен. Я попытался покурить SYS_CONNECT_BY_PATH() но и тут провал. Дальше этого уйти не смог. Это попытка с наскока победить иерархическую таблицу, но если тямы нет то победить вряд-ли получится. Я вообще не понимаю чтьо происходит на уровне запроса для работы с иерархией.
Код: |
SELECT LPAD(' ', 2*level-1)||SYS_CONNECT_BY_PATH(с_name, '/') "Path"
FROM Z#ADDRESS_OBJ
START WITH с_name = 'Республика'
CONNECT BY PRIOR c_parent_m = id;
|
|
В моей базе записей с именем "Республика" несколько, поэтому я выбрала только одну.
Вот иллюстрация вашего запроса, я его немного переписала:
Код: |
SELECT level, c_name, id, c_parent
, LPAD(' ', 4*level) || c_name
, LPAD(' ', 4*level) || id
, SYS_CONNECT_BY_PATH(c_name, ' -> ') "Path"
FROM Z#ADDRESS_OBJ
START WITH id = 3024091610 and c_name = 'Республика'
CONNECT BY PRIOR c_parent = id;
|
Найдите какую-нибудь запись с c_name = Республика, возьмите её id и подставьте в запрос вместо 3024091610.
Начинается всё с записи 3024091610. Она забирается в результат на основании условия start with.
Затем вторая итерация. Здесь работает условие из строки connect by. По ключевому слову prior определяем, что с чем сравниваем.
Выбрать записи, у которых id совпадает с c_parent предыдущей записи. Другими словами, выбрать цепочку родителей, начиная с указанной дочерней.
Ну или я->мать->бабка->прабабка, до тех пор, пока известен предок (c_parent).
sys_connect_path при этом возвращает всю цепочку поиска. Запустите и посмотрите.
Но эта цепочка тривиальна, примитивна и неинтересна.
Рассмотрим обратную, когда нужно выбрать
я -> дочь 1 -> внучка 1 -> правнучка 1
-> правнучка 2
-> дочь 2 ->
и так далее, в другую сторону, в сторону потомков.
Код: |
SELECT level, c_name, id, c_parent
, LPAD(' ', 4*level) || c_name
, LPAD(' ', 4*level) || id
, SYS_CONNECT_BY_PATH(c_name, ' -> ') "Path"
FROM Z#ADDRESS_OBJ
START WITH c_parent is null and id = 1918442
CONNECT BY c_parent = PRIOR id;
|
Аналогично, найдите какую-нибудь запись с c_parent is null и начните строить дерево с неё.
Всё начинается с записи id = 1918442.
Она забирается в результат и на основании неё осуществляется поиск записей, которые ссылаются на неё полем c_parent, то есть дочерних, тех, для которых я являюсь родителем.
Затем для каждой найденной дочерней записи повторяем процесс.
Красиво, правда?
Когда будете разбирать запросы, посмотрите, с какой стороны стоит prior. prior - указывает на запись с предыдущей итерации.
Рекурсивный запрос отдельно разбирать уже не сегодня, у нас уже поздний вечер. |
|
Madame, я снимаю шляпу перед вашим опытом и умом. Спасибо большое за бесценный ликбез. Благодаря вам иерархия теперь стала родной. Действительно получается очень красиво и изящно кроме того, судя по плану запроса конструкция CONNECT BY намного эффективнее, по крайней мере в моем случае.
Буду благодарен, если дадите небольшой урок по рекурсиям.
С Уважением,
Максим. |
|
 |
Эмиралька Эксперт
Вступление в Клуб: 09.11.2015
|
Пт Апр 14, 2023 09:46  Re: Помощь в рефакторинге |
|
Полезность: 3
|
MVZ пишет: |
Madame, я снимаю шляпу перед вашим опытом и умом. Спасибо большое за бесценный ликбез. Благодаря вам иерархия теперь стала родной. Действительно получается очень красиво и изящно кроме того, судя по плану запроса конструкция CONNECT BY намного эффективнее, по крайней мере в моем случае.
Буду благодарен, если дадите небольшой урок по рекурсиям.
С Уважением,
Максим. |
Спасибо. Буду признательна, если оцените мой труд ещё и нажатием кнопки "Оценить"
Теперь о рекурсии.
По сути, рекурсивный запрос - тот же иерархический, но разделённый на три части.
Вариант 1 - выяснить цепочку предков.
В иерархическом виде:
Код: | SELECT level, c_name, id, c_parent
, LPAD(' ', 4*level) || c_name
, LPAD(' ', 4*level) || id
, SYS_CONNECT_BY_PATH(c_name, ' -> ') "Path"
FROM Z#ADDRESS_OBJ
START WITH id = 3024091610 and c_name = 'Республика'
CONNECT BY PRIOR c_parent = id; |
в рекурсивном виде записывается так:
Код: | with /*___RECURSIVE___*/
hier(c_level, c_name, id, c_parent, c_hier_name, c_hier_id, c_path) as
(
select 1 C_LEVEL
, x.c_name
, x.id
, x.c_parent
, c_name as c_hier_name
, ''||id as c_hier_id
, ''||id as c_path
from Z#ADDRESS_OBJ x
where id = 3024091610 and c_name = 'Республика'
union all
select prior_.c_level+1 C_LEVEL
, x.c_name
, x.id
, x.c_parent
, lpad(' ', prior_.c_level*4, ' ') || x.c_name as c_hier_name
, lpad(' ', prior_.c_level*4, ' ') || x.id as c_hier_id
, prior_.c_path || ' -> ' || x.id as c_path
from Z#ADDRESS_OBJ x
, hier prior_
where prior_.c_parent = x.id
)
select *
from hier
; |
Обратите внимание, в первом запросе под операндом with стоит запрос, извлекающий данные стартовой записи, то есть той& которая в иерархическом запросе находится под операндом start with.
Второй запрос под оператондом with, который расположен после union all - определяет способ присоединения записей следующих итераций.
То есть то, что в иерархическом запросе стоит под операндом connect by.
Я специально выбрала алиас prior_, чтобы вам было удобнее сравнивать его с иерархическим, и вообще - советую.
После блока with находится запрос, выводящий полученные данные клиенту.
По-моему, тут всё понятно и без моих пояснений.
И пример с поиском дочек в рекурсивном виде выглядит так:
Код: |
with /*___RECURSIVE___*/
hier(c_level, c_name, id, c_parent, c_hier_name, c_hier_id, c_path) as
(
select 1 C_LEVEL
, x.c_name
, x.id
, x.c_parent
, c_name as c_hier_name
, ''||id as c_hier_id
, ''||id as c_path
from Z#ADDRESS_OBJ x
where id = 1918442
union all
select prior_.c_level+1 C_LEVEL
, x.c_name
, x.id
, x.c_parent
, lpad(' ', prior_.c_level*4, ' ') || x.c_name as c_hier_name
, lpad(' ', prior_.c_level*4, ' ') || x.id as c_hier_id
, prior_.c_path || ' -> ' || x.id as c_path
from Z#ADDRESS_OBJ x
, hier prior_
where x.c_parent = prior_.id
)
select *
from hier
order by c_path
;
|
Внимания заслуживает тот факт, что в иерархическом запросе сортировка по умолчанию выполняется в соответствии с построением (order siblings), а в рекурсивном - сначала выбираются все данные с итерации, и уже потом для каждой выполняется поиск данных следующей итерации. В общем, если убрать сортировку - будет видно. |
|
 |
MVZ Участник со стажем
Вступление в Клуб: 14.03.2023
|
Пт Апр 21, 2023 07:49  Re: Помощь в рефакторинге |
|
Полезность: Нет оценки
|
Эмиралька пишет: | MVZ пишет: |
Madame, я снимаю шляпу перед вашим опытом и умом. Спасибо большое за бесценный ликбез. Благодаря вам иерархия теперь стала родной. Действительно получается очень красиво и изящно кроме того, судя по плану запроса конструкция CONNECT BY намного эффективнее, по крайней мере в моем случае.
Буду благодарен, если дадите небольшой урок по рекурсиям.
С Уважением,
Максим. |
Спасибо. Буду признательна, если оцените мой труд ещё и нажатием кнопки "Оценить"
Теперь о рекурсии.
По сути, рекурсивный запрос - тот же иерархический, но разделённый на три части.
Вариант 1 - выяснить цепочку предков.
В иерархическом виде:
Код: | SELECT level, c_name, id, c_parent
, LPAD(' ', 4*level) || c_name
, LPAD(' ', 4*level) || id
, SYS_CONNECT_BY_PATH(c_name, ' -> ') "Path"
FROM Z#ADDRESS_OBJ
START WITH id = 3024091610 and c_name = 'Республика'
CONNECT BY PRIOR c_parent = id; |
в рекурсивном виде записывается так:
Код: | with /*___RECURSIVE___*/
hier(c_level, c_name, id, c_parent, c_hier_name, c_hier_id, c_path) as
(
select 1 C_LEVEL
, x.c_name
, x.id
, x.c_parent
, c_name as c_hier_name
, ''||id as c_hier_id
, ''||id as c_path
from Z#ADDRESS_OBJ x
where id = 3024091610 and c_name = 'Республика'
union all
select prior_.c_level+1 C_LEVEL
, x.c_name
, x.id
, x.c_parent
, lpad(' ', prior_.c_level*4, ' ') || x.c_name as c_hier_name
, lpad(' ', prior_.c_level*4, ' ') || x.id as c_hier_id
, prior_.c_path || ' -> ' || x.id as c_path
from Z#ADDRESS_OBJ x
, hier prior_
where prior_.c_parent = x.id
)
select *
from hier
; |
Обратите внимание, в первом запросе под операндом with стоит запрос, извлекающий данные стартовой записи, то есть той& которая в иерархическом запросе находится под операндом start with.
Второй запрос под оператондом with, который расположен после union all - определяет способ присоединения записей следующих итераций.
То есть то, что в иерархическом запросе стоит под операндом connect by.
Я специально выбрала алиас prior_, чтобы вам было удобнее сравнивать его с иерархическим, и вообще - советую.
После блока with находится запрос, выводящий полученные данные клиенту.
По-моему, тут всё понятно и без моих пояснений.
И пример с поиском дочек в рекурсивном виде выглядит так:
Код: |
with /*___RECURSIVE___*/
hier(c_level, c_name, id, c_parent, c_hier_name, c_hier_id, c_path) as
(
select 1 C_LEVEL
, x.c_name
, x.id
, x.c_parent
, c_name as c_hier_name
, ''||id as c_hier_id
, ''||id as c_path
from Z#ADDRESS_OBJ x
where id = 1918442
union all
select prior_.c_level+1 C_LEVEL
, x.c_name
, x.id
, x.c_parent
, lpad(' ', prior_.c_level*4, ' ') || x.c_name as c_hier_name
, lpad(' ', prior_.c_level*4, ' ') || x.id as c_hier_id
, prior_.c_path || ' -> ' || x.id as c_path
from Z#ADDRESS_OBJ x
, hier prior_
where x.c_parent = prior_.id
)
select *
from hier
order by c_path
;
|
Внимания заслуживает тот факт, что в иерархическом запросе сортировка по умолчанию выполняется в соответствии с построением (order siblings), а в рекурсивном - сначала выбираются все данные с итерации, и уже потом для каждой выполняется поиск данных следующей итерации. В общем, если убрать сортировку - будет видно. |
Эмиралька,
прошу прощения за задержку с ответом. Этот материал требовал скрупулезного изучения, но в итоге победа!
Большое спасибо еще раз. Благодаря вам тема раскрыта на 100%.
На всякий случай, для исторической ценности пишу ниже ваш код, просто с небольшими форматированиями, которые сделали его понимаение для меня проще.
Код: |
-----------------------
-- Пример с рекурсией
--
--После блока with находится запрос, выводящий полученные данные клиенту.
with /*___RECURSIVE___*/
m_hierarchy(c_level, c_name, id, c_parent, c_hier_name, c_hier_id, c_path) as
(
-- В первом запросе под операндом with стоит запрос, извлекающий данные стартовой записи,
-- то есть той которая в иерархическом запросе находится под операндом start with.
select 1 C_LEVEL
, x.c_name
, x.id
, x.c_parent
, c_name as c_hier_name
, ''||id as c_hier_id
, ''||id as c_path
from Z#ADDRESS_OBJ x
where id = 35431088 and c_name = 'Республика'
union all
-- Второй запрос расположен после union all
-- определяет способ присоединения записей следующих итераций. То есть то, что в иерархическом запросе стоит под операндом connect by.
-- алиас prior_, выбран для удобства сравния его с иерархическим, и вообще - советую.
select prior_.c_level+1 C_LEVEL
, x.c_name
, x.id
, x.c_parent
, lpad(' ', prior_.c_level*4, ' ') || x.c_name as c_hier_name
, lpad(' ', prior_.c_level*4, ' ') || x.id as c_hier_id
, prior_.c_path || ' -> ' || x.id as c_path
from Z#ADDRESS_OBJ x
, m_hierarchy prior_
where prior_.c_parent = x.id
)
select *
from m_hierarchy
;
--
|
|
|
 |
|
|
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете голосовать в опросах
|
|