Передача параметров в подзапрос с поднятием неверного идентификатора - PullRequest
2 голосов
/ 30 апреля 2019

У меня есть запрос, в котором я хочу сгруппировать по некоторым полям и объединить последнее поле в строке CSV. Если вы пришли с SQL Server, как я, вы бы использовали FOR XML PATH(''). Но в Oracle 12c это другая история:

Определение таблицы

CREATE TABLE HCF (
ID NUMBER,
HCF_DATE DATE,
HCF_TYPE_1 NUMBER,
HCF_TYPE_2 NUMBER)

Пример данных

ID  HCF_DATE    HCF_TYPE_1 HCF_TYPE_2
272 27/02/18    1          1
279 28/02/18    15         2
280 28/02/18    15         2
283 28/02/18    5          1

Я использую запрос

WITH CTE_HCF AS (
SELECT HCF_DATE, HCF_TYPE_1, HCF_TYPE_2, COUNT(ID)
FROM HCF
GROUP BY HCF_DATE, HCF_TYPE_1, HCF_TYPE_2
HAVING COUNT(ID) > 0
)

SELECT a.*, b.*
FROM CTE_HCF a
CROSS APPLY (
    SELECT LTRIM(MAX(SYS_CONNECT_BY_PATH(ORDRE_ID,',')) KEEP (DENSE_RANK LAST ORDER BY curr),',') AS ids
    FROM ( SELECT HCF_DATE, HCF_TYPE_1, HCF_TYPE_2, ID,
            ROW_NUMBER() OVER (PARTITION BY HCF_DATE ORDER BY HCF_TYPE_1, HCF_TYPE_2) AS curr,
            ROW_NUMBER() OVER (PARTITION BY HCF_DATE ORDER BY HCF_TYPE_1, HCF_TYPE_2) -1 AS prev
            FROM CTE_HCF
            WHERE HCF_DATE = a.HCF_DATE AND HCF_TYPE_1 = a.HCF_TYPE_1 AND HCF_TYPE_2 = a.HCF_TYPE_2
        )
    CONNECT BY prev = PRIOR curr
    AND HCF_DATE = PRIOR HCF_DATE
    AND HCF_TYPE_1 = PRIOR HCF_TYPE_1
    AND HCF_TYPE_2 = PRIOR HCF_TYPE_2
    START WITH curr = 1 ) b

Ошибка

ORA-00904: "a"."HCF_TYPE_2" :  invalid identifier

Желаемый вывод

HCF_DATE    HCF_TYPE_1 HCF_TYPE_2 IDS
27/02/18    1          1          272
28/02/18    15         2          279,280
28/02/18    5          1          283

Я думаю, что проблема в том, что поля из исходного запроса не отображаются в подзапросе подзапроса CROSS APPLY.

PS: я пробовал другие способы, описанные в этой статье , но мне не удалось по нескольким причинам, например, для LISTAGG он превышает количество символов xK. И у меня недостаточно прав для создания функции, и XMLAGG может привести к остановке вашего экземпляра Oracle.

Обновление Версия Oracle: Oracle Database 12c Enterprise Edition Release 12.1.0.2.0

Ответы [ 2 ]

1 голос
/ 01 мая 2019

Если вы превышаете ограничение на число символов для listagg, то, как вы справитесь с этим, зависит от того, что вы хотите отобразить.

Если вы используете 12.2, вы можете использовать условие сокращения переполнения для обрезки символов, которые превышают ограничение.

select hcf_date, hcf_type_1, hcf_type_2, 
       listagg ( id, ',' 
         on overflow truncate 
       ) within group ( 
         order by id
       ) csv
from   hcf
group  by hcf_date, hcf_type_1, hcf_type_2;

HCF_DATE               HCF_TYPE_1   HCF_TYPE_2   CSV       
27-FEB-2018 00:00:00                1             1 272        
28-FEB-2018 00:00:00                5             1 283        
28-FEB-2018 00:00:00               15             2 279,280 

Если вы используете 12.1, есть некоторыедругие обходные пути .

Вы можете использовать сопоставление с образцом строки (match_recognize), чтобы найти длину CSV для каждой дополнительной строки.И верните те, которые находятся в пределах строки:

Я добавил пару дополнительных строк и установил ограничение на 10 символов, чтобы показать принцип:

insert into hcf values ( 281, to_date('28/02/18', 'dd/mm/yy'), 15, 2);
insert into hcf values ( 282, to_date('28/02/18', 'dd/mm/yy'), 15, 2);

with grps as ( 
  select *
  from   hcf match_recognize (
    partition by hcf_date, hcf_type_1, hcf_type_2
    order by id
    measures 
      sum(lengthb(s.id) + lengthb(';')) as len
    all rows per match
    after match skip past last row
    pattern (s+)
    define 
      s as 1=1
  )
)
  select hcf_date, hcf_type_1, hcf_type_2,
         listagg ( id, ',' ) 
           within group ( 
             order by id
           ) csv
  from   grps
  where  len <= 10
  group  by hcf_date, hcf_type_1, hcf_type_2;

HCF_DATE               HCF_TYPE_1   HCF_TYPE_2  CSV       
27-FEB-2018 00:00:00            1            1  272        
28-FEB-2018 00:00:00            5            1  283        
28-FEB-2018 00:00:00           15            2  279,280    

Или вы можете разбить строкина отдельные группы, когда вы достигнете предела персонажа.И покажите их как отдельные CSV:

with grps as ( 
  select *
  from   hcf match_recognize (
    partition by hcf_date, hcf_type_1, hcf_type_2
    order by id
    measures 
      match_number() as grp
    all rows per match
    after match skip past last row
    pattern (s csv*)
    define csv as 
      lengthb(s.id) + sum(lengthb(csv.id) + lengthb(';')) < = 10
  )
)
  select hcf_date, hcf_type_1, hcf_type_2,
         listagg ( id, ',' ) 
           within group ( 
             order by id
           ) csv
  from   grps
  group  by hcf_date, hcf_type_1, hcf_type_2, grp;

HCF_DATE               HCF_TYPE_1   HCF_TYPE_2  CSV       
27-FEB-2018 00:00:00            1            1  272        
28-FEB-2018 00:00:00            5            1  283        
28-FEB-2018 00:00:00           15            2  279,280    
28-FEB-2018 00:00:00           15            2  281,282  

Если вы хотите вернуть весь список CSV длиннее, чем предел varchar2, вам нужно вернуть clob.Что вы можете сделать с XML:

select hcf_date, hcf_type_1, hcf_type_2,
       substr (
          xmlcast ( 
            xmlagg (
              xmlelement(s, ',' || id)
              order by id
            ) as clob
          ), 2
        ) csv
from    hcf
group  by hcf_date, hcf_type_1, hcf_type_2;  

HCF_DATE               HCF_TYPE_1   HCF_TYPE_2   CSV               
27-FEB-2018 00:00:00            1             1  272                
28-FEB-2018 00:00:00            5             1  283                
28-FEB-2018 00:00:00           15             2  279,280,281,282 
0 голосов
/ 10 мая 2019

Наконец мне удалось попросить администратора баз данных создать функцию, которая объединяет значения id в CSV, что, на мой взгляд, является лучшим подходом для возврата значения CLOB.

Функция:

create or replace FUNCTION concatenate_list (p_cursor IN  SYS_REFCURSOR)
  RETURN  CLOB
IS
  l_return  CLOB; 
  l_temp    CLOB;
BEGIN
  LOOP
    FETCH p_cursor
    INTO  l_temp;
    EXIT WHEN p_cursor%NOTFOUND;
    l_return := l_return || ',' || l_temp;
  END LOOP;
  RETURN LTRIM(l_return, ',');
END;

Запрос

WITH CTE_HCF AS (
SELECT HCF_DATE, HCF_TYPE_1, HCF_TYPE_2, COUNT(ID)
FROM HCF
GROUP BY HCF_DATE, HCF_TYPE_1, HCF_TYPE_2
HAVING COUNT(ID) > 0
)

SELECT a.*
  , concatenate_list(CURSOR(SELECT id FROM HCF WHERE HCF_DATE = a.HCF_DATE AND HCF_TYPE_1 = a.HCF_TYPE_1 AND HCF_TYPE_2 = a.HCF_TYPE_2)) AS CSV
FROM CTE_HCF a

Источник

PS: Если вам не нужен CLOB, тогда LISTAGG - ваш лучший выбор.

...