我有一个包含用户列表的表。
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
这是手动操作,每150个用户块中的每个用户都需要一个额外的UNION,以后用户总数可能会增加。
是否可以这样做,以便动态生成user_id的定界字符串,使其适合4000个字符的多个块,并且不会将user_id拆分为多个字符串?
理想情况下,我希望输出看起来像这样:
USER1, USER2, USER3 (to) USER149
USER150, USER151, USER152 (to) USER300
USER301, USER302, USER303 (to) USER450`
该解决方案需要为SELECT语句,因为该模式是只读的,并且我们无法在数据库上创建任何对象。我们正在使用Oracle 11g。
答案 0 :(得分:0)
您可以使用流水线函数来做到这一点:
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);
答案 1 :(得分:0)
使用以下功能的替代方法:
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;
答案 2 :(得分:0)
我相信下面的SQL在大多数情况下都可以使用。我已经对SQL进行了硬编码,以将字符串分解为150个用户ID条目,但其余都是动态的。
中间部分产生重复项,这需要额外的区别来消除,但是我不确定是否有更好的方法来做到这一点。
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