Динамически разделенные строки в несколько разделенных запятыми списков - PullRequest
0 голосов
/ 07 ноября 2018

У меня есть таблица со списком пользователей.

USER_TABLE

USER_ID   DEPT
-------   ----
USER1     HR
USER2     FINANCE
USER3     IT`

Используя оператор SQL, мне нужно получить список пользователей в виде строки с разделителями, возвращаемой как varchar2 - это единственный тип данных, который я могу использовать в соответствии с приложением, которое я использую, например,

USER1, USER2, USER3

У меня проблема с тем, что список будет превышать 4000 символов. У меня есть следующее, которое будет вручную объединять пользователей в списки по 150 пользователей за раз (исходя из максимального размера user_id, равного 20 символам, и разделителей, которые можно уместить в 4000 символов).

SELECT  LISTAGG(USER_ID, ',') WITHIN GROUP (ORDER BY USER_ID) 
FROM (SELECT DISTINCT USER_ID  AS USER_ID, ROW_NUMBER() OVER (ORDER BY USER_ID) RN FROM TABLE_NAME)
WHERE RN <= 150 
START WITH RN = 1
CONNECT BY PRIOR RN = RN - 1
UNION
SELECT  LISTAGG(USER_ID, ',') WITHIN GROUP (ORDER BY USER_ID) 
FROM (SELECT DISTINCT USER_ID  AS USER_ID, ROW_NUMBER() OVER (ORDER BY USER_ID) RN FROM TABLE_NAME)
WHERE RN > 150 AND RN <= 300 
START WITH RN = 1
CONNECT BY PRIOR RN = RN - 1

Это руководство, для которого потребуется дополнительный UNION для каждого блока из 150 пользователей, и общее число пользователей может увеличиться в более поздний срок.

Возможно ли сделать это так, чтобы разделенные строки user_ids генерировались динамически, чтобы они вписывались в несколько фрагментов по 4000 символов и ни один user_ids не разделялся на несколько строк?

В идеале, я бы хотел, чтобы результат выглядел так:

USER1, USER2, USER3 (to) USER149
USER150, USER151, USER152 (to) USER300
USER301, USER302, USER303 (to) USER450`

Решением должен быть оператор SELECT, поскольку схема доступна только для чтения, и мы не можем создавать объекты в базе данных. Мы используем Oracle 11g.

Ответы [ 3 ]

0 голосов
/ 07 ноября 2018

Альтернативный способ с использованием функции ниже:

create or replace FUNCTION my_agg_user 
RETURN CLOB IS
  l_string CLOB;

  TYPE t_bulk_collect_test_tab IS TABLE OF VARCHAR2(4000);
  l_tab    t_bulk_collect_test_tab;

CURSOR user_list IS
SELECT  USER_ID
FROM USER_TABLE ;

BEGIN

  OPEN user_list;
  LOOP
   FETCH user_list
    BULK COLLECT INTO l_tab LIMIT 1000;
  FOR indx IN 1 .. l_tab.COUNT 
   LOOP
   l_string := l_string || l_tab(indx);
   l_string := l_string || ','; 
   END LOOP;
      EXIT WHEN user_list%NOTFOUND;
  END LOOP;
  CLOSE user_list;
  RETURN l_string;
END my_agg_user;

После создания функции,

select my_agg_user from dual;
0 голосов
/ 07 ноября 2018

Я полагаю, что приведенный ниже SQL должен работать в большинстве случаев. Я жестко запрограммировал SQL, чтобы разбить строки на 150 записей идентификатора пользователя, но остальное - динамическое.

Средняя часть создает дубликаты, для устранения которых требуется дополнительный отчет, но я не уверен, что есть лучший способ сделать это.

WITH POSITION AS ( SELECT  ((LEVEL-1) * 150 + 1) FROM_POS, LEVEL * 150 TO_POS
  FROM DUAL
CONNECT BY LEVEL <= (SELECT COUNT(DISTINCT( USER_ID)) / 150 FROM TABLE_NAME)
)
SELECT  DISTINCT
LISTAGG(USER_ID, ',') WITHIN GROUP (ORDER BY USER_ID) OVER (PARTITION BY FROM_POS, TO_POS)
FROM 
(SELECT DISTINCT USER_ID  AS USER_ID, ROW_NUMBER() OVER (ORDER BY USER_ID) RN FROM TABLE_NAME) V0 , 
POSITION 
WHERE V0.RN >= POSITION.FROM_POS 
AND V0.RN <=  POSITION.TO_POS
0 голосов
/ 07 ноября 2018

Вы можете сделать это с помощью конвейерной функции:

create or replace function get_user_ids
    return sys.dbms_debug_vc2coll pipelined
is
    rv varchar2(4000) := null;
begin
    for r in ( select user_id, length(user_id) as lng
               from user_table
               order by user_id )
    loop
        if length(rv) + r.lng + 1 > 4000
        then
            rv := rtrim(rv, ','); -- remove trailing comma
            pipe row (rv);
            rv := null;
        end if;
        rv := rv || r.user_id || ',';
    end loop;
    return;
end;
/

Вы бы назвали это так:

select column_value as user_id_csv
from table(get_user_ids);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...