J'utilise Oracle SQL Developer version 3.0.04. J'ai essayé d'utiliser la fonction LISTAGG
pour regrouper les données.
CREATE TABLE FINAL_LOG AS
SELECT SESSION_DT, C_IP, CS_USER_AGENT,
listagg(WEB_LINK, ' ')
WITHIN GROUP(ORDER BY C_IP, CS_USER_AGENT) "WEB_LINKS"
FROM webviews
GROUP BY C_IP, CS_USER_AGENT, SESSION_DT
ORDER BY SESSION_DT
Cependant, je continue à avoir l'erreur,
SQL Error: ORA-01489: result of string concatenation is too long
Je suis à peu près sûr que la sortie risque d'être supérieure à 4 000, car le WEB_LINK mentionné ici est une valeur concaténée de racine d'URL et de requête d'URL.
Y a-t-il un moyen de le contourner ou existe-t-il une autre solution?
Étant donné que la chaîne d'agrégats peut comporter plus de 4 000 octets, vous ne pouvez pas utiliser la fonction LISTAGG
. Vous pourriez éventuellement créer une fonction d'agrégat définie par l'utilisateur qui renvoie CLOB
au lieu de VARCHAR2
. Il existe un exemple d'agrégat défini par l'utilisateur qui renvoie CLOB
dans la discussion askTom d'origine à laquelle Tim fait référence depuis la première discussion.
SELECT RTRIM(XMLAGG(XMLELEMENT(E,colname,',').EXTRACT('//text()') ORDER BY colname).GetClobVal(),',') AS LIST
FROM tablename;
Cela retournera une valeur de clob, donc aucune limite sur les lignes.
Vous dépassez la limite SQL de 4000 octets qui s'applique également à LISTAGG
.
SQL> SELECT listagg(text, ',') WITHIN GROUP (
2 ORDER BY NULL)
3 FROM
4 (SELECT to_char(to_date(level,'j'), 'jsp') text FROM dual CONNECT BY LEVEL < 250
5 )
6 /
SELECT listagg(text, ',') WITHIN GROUP (
*
ERROR at line 1:
ORA-01489: result of string concatenation is too long
Pour résoudre ce problème, vous pouvez utiliserXMLAGG.
Par exemple,
SQL> SET LONG 2000000
SQL> SET pagesize 50000
SQL> SELECT rtrim(xmlagg(XMLELEMENT(e,text,',').EXTRACT('//text()')
2 ).GetClobVal(),',') very_long_text
3 FROM
4 (SELECT to_char(to_date(level,'j'), 'jsp') text FROM dual CONNECT BY LEVEL < 250
5 )
6 /
VERY_LONG_TEXT
--------------------------------------------------------------------------------
one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thirteen,fourteen
,fifteen,sixteen,seventeen,eighteen,nineteen,twenty,twenty-one,twenty-two,twenty
-three,twenty-four,twenty-five,twenty-six,twenty-seven,twenty-eight,twenty-nine,
thirty,thirty-one,thirty-two,thirty-three,thirty-four,thirty-five,thirty-six,thi
rty-seven,thirty-eight,thirty-nine,forty,forty-one,forty-two,forty-three,forty-f
our,forty-five,forty-six,forty-seven,forty-eight,forty-nine,fifty,fifty-one,fift
y-two,fifty-three,fifty-four,fifty-five,fifty-six,fifty-seven,fifty-eight,fifty-
nine,sixty,sixty-one,sixty-two,sixty-three,sixty-four,sixty-five,sixty-six,sixty
-seven,sixty-eight,sixty-nine,seventy,seventy-one,seventy-two,seventy-three,seve
nty-four,seventy-five,seventy-six,seventy-seven,seventy-eight,seventy-nine,eight
y,eighty-one,eighty-two,eighty-three,eighty-four,eighty-five,eighty-six,eighty-s
even,eighty-eight,eighty-nine,ninety,ninety-one,ninety-two,ninety-three,ninety-f
our,ninety-five,ninety-six,ninety-seven,ninety-eight,ninety-nine,one hundred,one
hundred one,one hundred two,one hundred three,one hundred four,one hundred five
,one hundred six,one hundred seven,one hundred eight,one hundred nine,one hundre
d ten,one hundred eleven,one hundred twelve,one hundred thirteen,one hundred fou
rteen,one hundred fifteen,one hundred sixteen,one hundred seventeen,one hundred
eighteen,one hundred nineteen,one hundred twenty,one hundred twenty-one,one hund
red twenty-two,one hundred twenty-three,one hundred twenty-four,one hundred twen
ty-five,one hundred twenty-six,one hundred twenty-seven,one hundred twenty-eight
,one hundred twenty-nine,one hundred thirty,one hundred thirty-one,one hundred t
hirty-two,one hundred thirty-three,one hundred thirty-four,one hundred thirty-fi
ve,one hundred thirty-six,one hundred thirty-seven,one hundred thirty-eight,one
hundred thirty-nine,one hundred forty,one hundred forty-one,one hundred forty-tw
o,one hundred forty-three,one hundred forty-four,one hundred forty-five,one hund
red forty-six,one hundred forty-seven,one hundred forty-eight,one hundred forty-
nine,one hundred fifty,one hundred fifty-one,one hundred fifty-two,one hundred f
ifty-three,one hundred fifty-four,one hundred fifty-five,one hundred fifty-six,o
ne hundred fifty-seven,one hundred fifty-eight,one hundred fifty-nine,one hundre
d sixty,one hundred sixty-one,one hundred sixty-two,one hundred sixty-three,one
hundred sixty-four,one hundred sixty-five,one hundred sixty-six,one hundred sixt
y-seven,one hundred sixty-eight,one hundred sixty-nine,one hundred seventy,one h
undred seventy-one,one hundred seventy-two,one hundred seventy-three,one hundred
seventy-four,one hundred seventy-five,one hundred seventy-six,one hundred seven
ty-seven,one hundred seventy-eight,one hundred seventy-nine,one hundred eighty,o
ne hundred eighty-one,one hundred eighty-two,one hundred eighty-three,one hundre
d eighty-four,one hundred eighty-five,one hundred eighty-six,one hundred eighty-
seven,one hundred eighty-eight,one hundred eighty-nine,one hundred ninety,one hu
ndred ninety-one,one hundred ninety-two,one hundred ninety-three,one hundred nin
ety-four,one hundred ninety-five,one hundred ninety-six,one hundred ninety-seven
,one hundred ninety-eight,one hundred ninety-nine,two hundred,two hundred one,tw
o hundred two,two hundred three,two hundred four,two hundred five,two hundred si
x,two hundred seven,two hundred eight,two hundred nine,two hundred ten,two hundr
ed eleven,two hundred twelve,two hundred thirteen,two hundred fourteen,two hundr
ed fifteen,two hundred sixteen,two hundred seventeen,two hundred eighteen,two hu
ndred nineteen,two hundred twenty,two hundred twenty-one,two hundred twenty-two,
two hundred twenty-three,two hundred twenty-four,two hundred twenty-five,two hun
dred twenty-six,two hundred twenty-seven,two hundred twenty-eight,two hundred tw
enty-nine,two hundred thirty,two hundred thirty-one,two hundred thirty-two,two h
undred thirty-three,two hundred thirty-four,two hundred thirty-five,two hundred
thirty-six,two hundred thirty-seven,two hundred thirty-eight,two hundred thirty-
nine,two hundred forty,two hundred forty-one,two hundred forty-two,two hundred f
orty-three,two hundred forty-four,two hundred forty-five,two hundred forty-six,t
wo hundred forty-seven,two hundred forty-eight,two hundred forty-nine
Si vous voulez concaténer plusieurs colonnes qui ont lui-même 4000 octets , vous pouvez concaténer la sortie XMLAGG de chaque colonne pour éviter la limite SQL de 4000 octets.
Par exemple,
WITH DATA AS
( SELECT 1 id, rpad('a1',4000,'*') col1, rpad('b1',4000,'*') col2 FROM dual
UNION
SELECT 2 id, rpad('a2',4000,'*') col1, rpad('b2',4000,'*') col2 FROM dual
)
SELECT ID,
rtrim(xmlagg(XMLELEMENT(e,col1,',').EXTRACT('//text()') ).GetClobVal(), ',')
||
rtrim(xmlagg(XMLELEMENT(e,col2,',').EXTRACT('//text()') ).GetClobVal(), ',')
AS very_long_text
FROM DATA
GROUP BY ID
ORDER BY ID;
listagg
a récemment été couvert par le standard ISO SQL (SQL: 2016). Dans ce cadre, il a également obtenu une clause on overflow
, prise en charge par Oracle 12cR2.
LISTAGG(<expression>, <separator> ON OVERFLOW …)
La clause on overflow
prend en charge une option truncate
(à la place du comportement par défaut on overflow error
).
ON OVERFLOW TRUNCATE [<filler>] WITH[OUT] COUNT
La valeur optionnelle par défaut est trois points (...) et sera ajoutée comme dernier élément en cas de troncature.
Si with count est spécifié et que la troncature se produit, le nombre de valeurs omises est placé entre parenthèses et ajouté au résultat.
En savoir plus sur la clause on overflow
de listagg
: http://modern-sql.com/feature/listagg
Une nouvelle fonctionnalité ajoutée dans 12cR2 est la clause ON OVERFLOW
de LISTAGG
. La requête incluant cette clause ressemblerait à ceci:
SELECT pid, LISTAGG(Desc, ' ' ON OVERFLOW TRUNCATE ) WITHIN GROUP (ORDER BY seq) AS desc
FROM B GROUP BY pid;
Ce qui précède limitera la sortie à 4000 caractères, mais ne générera pas l'erreur ORA-01489
.
Voici quelques-unes des options supplémentaires de la clause ON OVERFLOW
:
ON OVERFLOW TRUNCATE 'Contd..'
: Ceci affichera 'Contd..'
à la fin de la chaîne (la valeur par défaut est ...
)ON OVERFLOW TRUNCATE ''
: Ceci affichera les 4000 caractères sans chaîne de fin.ON OVERFLOW TRUNCATE WITH COUNT
: Ceci affichera le nombre total de caractères à la fin après les caractères finaux . Exemple: - '...(5512)
'ON OVERFLOW ERROR
: Si vous vous attendez à ce que LISTAGG
échoue avec l'erreur ORA-01489
(qui est par défaut de toute façon).Je pouvais tolérer que mon champ soit enchaîné sur plusieurs lignes, chacune étant inférieure à la limite de 4000 caractères - procédez comme suit:
with PRECALC as (select
floor(4000/(max(length(MY_COLUMN)+LENGTH(',')))) as MAX_FIELD_LENGTH
from MY_TABLE)
select LISTAGG(MY_COLUMN,',') WITHIN GROUP(ORDER BY floor(rownum/MAX_FIELD_LENGTH), MY_COLUMN)
from MY_TABLE, PRECALC
group by floor(rownum/MAX_FIELD_LENGTH)
;
Gestion des débordements dans LISTAGG
Nous pouvons utiliser la fonction de correspondance de modèle SQL de la base de données 12c, MATCH_RECOGNIZE, pour renvoyer une liste de valeurs ne dépassant pas la limite.
Exemple de code et plus d'explications dans le lien ci-dessous.
https://blogs.Oracle.com/datawarehousing/entry/managing_overflows_in_listagg
Ajoutant à la réponse acceptée ... J'ai rencontré un problème similaire et fini par utiliser une fonction définie par l'utilisateur qui renvoyait clob au lieu de varchar2. Voici ma solution:
CREATE OR REPLACE TYPE temp_data FORCE AS OBJECT
(
temporary_data NVARCHAR2(4000)
)
/
CREATE OR REPLACE TYPE temp_data_table FORCE AS TABLE OF temp_data;
/
CREATE OR REPLACE FUNCTION my_agg_func (p_temp_data_table IN temp_data_table, p_delimiter IN NVARCHAR2)
RETURN CLOB IS
l_string CLOB;
BEGIN
FOR i IN p_temp_data_table.FIRST .. p_temp_data_table.LAST LOOP
IF i != p_temp_data_table.FIRST THEN
l_string := l_string || p_delimiter;
END IF;
l_string := l_string || p_temp_data_table(i).temporary_data;
END LOOP;
RETURN l_string;
END my_agg_func;
/
Maintenant, au lieu de faire
LISTAGG(column_to_aggregate, '#any_delimiter#') WITHIN GROUP (ORDER BY column_to_order_by)
Je dois faire ça
my_agg_func (
cast(
collect(
temp_data(column_to_aggregate)
order by column_to_order_by
) as temp_data_table
),
'#any_delimiter#'
)
Dans certains scénarios, l’intention est d’obtenir toutes les clés DISTINCT LISTAGG et le débordement est provoqué par le fait que LISTAGG concatène TOUTES LES CL&EACUTE;S.
Voici un petit exemple
create table tab as
select
trunc(rownum/10) x,
'GRP'||to_char(mod(rownum,4)) y,
mod(rownum,10) z
from dual connect by level < 100;
select
x,
LISTAGG(y, '; ') WITHIN GROUP (ORDER BY y) y_lst
from tab
group by x;
X Y_LST
---------- ------------------------------------------------------------------
0 GRP0; GRP0; GRP1; GRP1; GRP1; GRP2; GRP2; GRP3; GRP3
1 GRP0; GRP0; GRP1; GRP1; GRP2; GRP2; GRP2; GRP3; GRP3; GRP3
2 GRP0; GRP0; GRP0; GRP1; GRP1; GRP1; GRP2; GRP2; GRP3; GRP3
3 GRP0; GRP0; GRP1; GRP1; GRP2; GRP2; GRP2; GRP3; GRP3; GRP3
4 GRP0; GRP0; GRP0; GRP1; GRP1; GRP1; GRP2; GRP2; GRP3; GRP3
5 GRP0; GRP0; GRP1; GRP1; GRP2; GRP2; GRP2; GRP3; GRP3; GRP3
6 GRP0; GRP0; GRP0; GRP1; GRP1; GRP1; GRP2; GRP2; GRP3; GRP3
7 GRP0; GRP0; GRP1; GRP1; GRP2; GRP2; GRP2; GRP3; GRP3; GRP3
8 GRP0; GRP0; GRP0; GRP1; GRP1; GRP1; GRP2; GRP2; GRP3; GRP3
9 GRP0; GRP0; GRP1; GRP1; GRP2; GRP2; GRP2; GRP3; GRP3; GRP3
Si les groupes sont grands, les touches répétées atteignent rapidement la longueur maximale autorisée et vous obtenez le ORA-01489: result of string concatenation is too long
.
Malheureusement, LISTAGG( DISTINCT y, '; ')
n'est pas pris en charge, mais vous pouvez utiliser le fait que LISTAGG ignore les valeurs NULL. En utilisant le ROW_NUMBER, nous ne considérerons que la première clé.
with rn as (
select x,y,z,
row_number() over (partition by x,y order by y) rn
from tab
)
select
x,
LISTAGG( case when rn = 1 then y end, '; ') WITHIN GROUP (ORDER BY y) y_lst,
sum(z) z
from rn
group by x
order by x;
X Y_LST Z
---------- ---------------------------------- ----------
0 GRP0; GRP1; GRP2; GRP3 45
1 GRP0; GRP1; GRP2; GRP3 45
2 GRP0; GRP1; GRP2; GRP3 45
3 GRP0; GRP1; GRP2; GRP3 45
4 GRP0; GRP1; GRP2; GRP3 45
5 GRP0; GRP1; GRP2; GRP3 45
6 GRP0; GRP1; GRP2; GRP3 45
7 GRP0; GRP1; GRP2; GRP3 45
8 GRP0; GRP1; GRP2; GRP3 45
9 GRP0; GRP1; GRP2; GRP3 45
Bien entendu, le même résultat peut être obtenu en utilisant GROUP BY x,y
dans la sous-requête. L’avantage de ROW_NUMBER
est que toutes les autres fonctions d’agrégat peuvent être utilisées comme illustré avec SUM(z)
.
Nous avons pu résoudre un problème similaire ici en utilisant Oracle LISTAGG. Il y a eu un moment où ce que nous regroupions dépassait la limite de 4 Ko, mais cela a été facilement résolu en faisant en sorte que le premier ensemble de données utilise les 15 premiers éléments à agréger, chacun ayant une limite de 256 Ko.
Plus d'infos: Nous avons des projets, qui ont des ordres de modification, qui ont à leur tour des explications. Pourquoi la base de données est-elle configurée pour accepter le texte de modification en morceaux de 256 Ko n'est pas connue, mais c'est l'une des contraintes de conception. Ainsi, l'application qui introduit les explications de modification dans la table s'arrête à 254K et insère, puis obtient le prochain groupe de texte et si> 254K génère une autre ligne, etc. Nous avons donc un projet avec un ordre de modification, un 1: 1. Ensuite, nous avons 1: n pour des explications. LISTAGG concatène tous ces éléments. Nous avons les valeurs RMRKS_SN, 1 pour chaque remarque et/ou pour chaque 254 Ko de caractères.
Le plus grand RMRKS_SN s’est avéré être 31, alors j’ai fait le premier jeu de données en extrayant SN 0 à 15, le deuxième jeu de données 16 à 30 et le dernier jeu de données 31 à 45 - hé, prévoyons que quelqu'un ajoute BEAUCOUP d’explications à certains changements ordres!
Dans le rapport SQL, le Tablix est lié au premier jeu de données. Pour obtenir les autres données, voici l'expression:
= Premier (Champs! NON_STD_TXT.Value, "DataSet_EXPLAN") & Premier (Champs! NON_STD_TXT.Value, "ds_EXPLAN_SN_16_TO_30") & Premier (Champs! NON_STD_TXT.Value, "ds_EXPLAN_SN_31_D
Pour nous, le groupe de base de données doit créer des fonctions, etc. à cause de contraintes de sécurité. Donc, avec un peu de créativité, nous n’avions pas à créer un User Aggregate ou un UDF.
Si votre application dispose d'une sorte de SN à agréger, cette méthode devrait fonctionner. Je ne sais pas ce qu'est l'équivalent TSQL - nous avons la chance de traiter avec Oracle pour ce rapport, pour lequel LISTAGG est une aubaine.
Le code est:
SELECT
LT.C_O_NBR AS LT_CO_NUM,
RT.C_O_NBR AS RT_CO_NUM,
LT.STD_LN_ITM_NBR,
RT.NON_STD_LN_ITM_NBR,
RT.NON_STD_PRJ_NBR,
LT.STD_PRJ_NBR,
NVL(LT.PRPSL_LN_NBR, RT.PRPSL_LN_NBR) AS PRPSL_LN_NBR,
LT.STD_CO_EXPL_TXT AS STD_TXT,
LT.STD_CO_EXPLN_T,
LT.STD_CO_EXPL_SN,
RT.NON_STD_CO_EXPLN_T,
LISTAGG(RT.RMRKS_TXT_FLD, '')
WITHIN GROUP(ORDER BY RT.RMRKS_SN) AS NON_STD_TXT
FROM ...
WHERE RT.RMRKS_SN BETWEEN 0 AND 15
GROUP BY
LT.C_O_NBR,
RT.C_O_NBR,
...
Et dans les 2 autres jeux de données, il suffit de sélectionner le LISTAGG uniquement pour les sous-requêtes du FROM:
SELECT
LISTAGG(RT.RMRKS_TXT_FLD, '')
WITHIN GROUP(ORDER BY RT.RMRKS_SN) AS NON_STD_TXT
DE ...
WHERE RT.RMRKS_SN BETWEEN 31 AND 45
...
... etc.