CftClub.ru
Клуб специалистов ЦФТ-Банк

Помощь в рефакторинге

 
Ответить на тему    Клуб специалистов ЦФТ-Банк (IBSO) -> Уроки ЦФТ-Банк для начинающих
Предыдущая тема :: Следующая тема  
Автор Сообщение
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.

Вроде всё описала...

Могу на примере ещё проиллюстрировать. надо?


Эмиралька, спасибо вам большое за подробное описание и время, стало немного понятнее, но немного ... (скорее всего в силу моей природной тупости Laughing ) Да, если сможете дать пример для "особо-одаренных" я буду вам очень признателен. Я попытался покурить 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, запустил - работает. Вспомнил как в детском саду к нам приходил фокусник, было непонятно что он делал но смотреть было безумно интересно Very Happy - это лирика. Что касается уважения к вашему ответу и времени, я для себя попытался разложить код, что бы было немного понятнее его анализировать, получилось вот так
Код:

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] не пойму что это зачем и во что превращается
Код:
 ','||b1.ID||',' A$4
Код:
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 пишет:


Эмиралька, спасибо вам большое за подробное описание и время, стало немного понятнее, но немного ... (скорее всего в силу моей природной тупости Laughing ) Да, если сможете дать пример для "особо-одаренных" я буду вам очень признателен. Я попытался покурить 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 пишет:


Эмиралька, спасибо вам большое за подробное описание и время, стало немного понятнее, но немного ... (скорее всего в силу моей природной тупости Laughing ) Да, если сможете дать пример для "особо-одаренных" я буду вам очень признателен. Я попытался покурить 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 пишет:


Эмиралька, спасибо вам большое за подробное описание и время, стало немного понятнее, но немного ... (скорее всего в силу моей природной тупости Laughing ) Да, если сможете дать пример для "особо-одаренных" я буду вам очень признателен. Я попытался покурить 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 намного эффективнее, по крайней мере в моем случае.
Буду благодарен, если дадите небольшой урок по рекурсиям.

С Уважением,
Максим.


Спасибо. Буду признательна, если оцените мой труд ещё и нажатием кнопки "Оценить" Smile

Теперь о рекурсии.
По сути, рекурсивный запрос - тот же иерархический, но разделённый на три части.

Вариант 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 намного эффективнее, по крайней мере в моем случае.
Буду благодарен, если дадите небольшой урок по рекурсиям.

С Уважением,
Максим.


Спасибо. Буду признательна, если оцените мой труд ещё и нажатием кнопки "Оценить" Smile

Теперь о рекурсии.
По сути, рекурсивный запрос - тот же иерархический, но разделённый на три части.

Вариант 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
;
--
Показать сообщения:   
Ответить на тему    Клуб специалистов ЦФТ-Банк (IBSO) -> Уроки ЦФТ-Банк для начинающих Часовой пояс: GMT + 3
Страница 1 из 1

 
Перейти:  
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Рейтинг@Mail.ru