Estoy tratando de usar la LISTAGG
función en Oracle. Me gustaría obtener solo los valores distintos para esa columna. ¿Hay alguna forma en la que pueda obtener solo los valores distintos sin crear una función o un procedimiento?
col1 col2 Creado_por 1 2 Smith 1 2 Juan 1 3 Ajay 1 4 carnero 1 5 Jack
Necesito seleccionar col1 y la LISTAGG
de col2 (la columna 3 no se considera). Cuando hago eso, obtengo algo como esto como resultado de LISTAGG
: [2,2,3,4,5]
Necesito eliminar el '2' duplicado aquí; Solo necesito los valores distintos de col2 contra col1.
sql
oracle
aggregate-functions
listagg
Priyanth
fuente
fuente
Respuestas:
19c y posteriores:
18c y anteriores:
Si necesita más columnas, algo como esto podría ser lo que está buscando:
fuente
listagg
es la única función agregada en la consulta, debería hacerlo. Sin embargo, combinarlo con otras funciones agregadas es más complicado.A continuación, le indicamos cómo resolver su problema.
devoluciones
2,2.1,3,4
De oráculo 19C está integrado, ver aquí
A partir del 18C y antes, pruebe dentro del grupo, consulte aquí
De lo contrario, use expresiones regulares
Responda abajo:
Nota: Lo anterior funcionará en la mayoría de los casos; la lista debe estar ordenada, es posible que deba recortar todos los espacios iniciales y finales según sus datos.
Si tiene muchos elementos en un grupo> 20 o tamaños de cadena grandes, es posible que se encuentre con el límite de tamaño de cadena de Oracle 'el resultado de la concatenación de cadenas es demasiado largo'.
Desde Oracle 12cR2 puede suprimir este error, consulte aquí . Alternativamente, ponga un número máximo de miembros en cada grupo. Esto solo funcionará si está bien enumerar solo los primeros miembros. Si tiene cadenas de variables muy largas, es posible que esto no funcione. tendrás que experimentar.
Otra solución (no tan simple) para, con suerte, evitar el límite de tamaño de la cadena de Oracle : el tamaño de la cadena está limitado a 4000. Gracias a esta publicación aquí por user3465996
V1 - algunos casos de prueba - FYI
V2: elementos contenidos en elementos, por ejemplo. 2,21
v3 - ¡regex gracias a Igor! funciona en todos los casos.
fuente
ORA-01489: result of string concatenation is too long
.a,b,b,b,b,c
se convertiría ena,b,b,c
:-( (Oracle 11.2)regexp_replace(your_string, '([^,]+)(,\1)*(,|$)', '\1\3')
puede utilizar la
wm_concat
función indocumentada .esta función devuelve la columna clob, si lo desea, puede usar
dbms_lob.substr
para convertir clob a varchar2.fuente
wm_concat(distinct x)
?wm_concat
. Consulte ¿Por qué no utilizar la función WM_CONCAT en Oracle? .Superé este problema agrupando los valores primero, luego hago otra agregación con el listagg. Algo como esto:
solo un acceso completo a la tabla, relativamente fácil de expandir a consultas más complejas
fuente
Si la intención es aplicar esta transformación a múltiples columnas, he extendido la solución a_horse_with_no_name:
Esta es la versión 11.2.0.2.0 de Oracle Database 11g Enterprise Edition - Producción de 64 bits.
No pude usar STRAGG porque no hay forma de DISTINCT y ORDER.
El rendimiento escala linealmente, lo cual es bueno, ya que estoy agregando todas las columnas de interés. Lo anterior tomó 3 segundos para 77K filas. Por solo un resumen, .172 segundos. Lo hago con que había una manera de diferenciar varias columnas en una tabla en una sola pasada.
fuente
Si desea valores distintos en MÚLTIPLES columnas, desea control sobre el orden de clasificación, no desea utilizar una función no documentada que pueda desaparecer y no desea más de un escaneo completo de la tabla, puede encontrar útil esta construcción:
fuente
¿Qué hay de la creación de una función dedicada que hará la parte "distinta":
Y luego utilícelo para hacer la agregación:
fuente
Para solucionar el problema de la longitud de la cadena, puede usar
XMLAGG
que es similarlistagg
pero devuelve un clob.Luego puede analizar usando
regexp_replace
y obtener los valores únicos y luego volverlos a convertir en una cadena usandodbms_lob.substr()
. Si tiene una gran cantidad de valores distintos, aún se quedará sin espacio de esta manera, pero en muchos casos el código siguiente debería funcionar.También puede cambiar los delimitadores que usa. En mi caso, quería '-' en lugar de ',' pero debería poder reemplazar los guiones en mi código y usar comas si lo desea.
fuente
Refinando aún más la corrección de @ YoYo al enfoque basado en row_number () de @ a_horse_with_no_name usando DECODE vs CASE (lo vi aquí ). Veo que @Martin Vrbovsky también tiene esta respuesta de enfoque de caso.
fuente
El próximo Oracle 19c será compatible
DISTINCT
conLISTAGG
.EDITAR:
Oracle 19C LISTAGG DISTINCT
fuente
¿Alguien ha pensado en usar una cláusula PARTITION BY? Me funcionó en esta consulta para obtener una lista de los servicios de la aplicación y el acceso.
Tuve que eliminar mi cláusula where para NDA, pero entiendes la idea.
fuente
LISTAGG
. Parece que solo tendría unoT.ACCESS_MODE
por fila ya que está agrupando por él.Creo que esto podría ayudar: CASE el valor de las columnas en NULL si está duplicado, entonces no se adjunta a la cadena LISTAGG:
Resultados en:
fuente
listagg () ignora los valores NULL, por lo que en un primer paso podría usar la función lag () para analizar si el registro anterior tenía el mismo valor, si es así, entonces NULL, de lo contrario 'nuevo valor'.
Resultados
Tenga en cuenta que el segundo 2 se reemplaza por NULL. Ahora puede envolver un SELECT con listagg () alrededor.
Resultado
También puede hacer esto en varias columnas.
Resultado
fuente
Puede hacerlo mediante el reemplazo de RegEx. Aquí hay un ejemplo:
También publicado aquí: Oracle - valores únicos de Listagg
fuente
Utilice la función listagg_clob creada así:
fuente
Escribí una función para manejar esto usando expresiones regulares. Los parámetros de entrada son: 1) la llamada listagg en sí misma 2) Una repetición del delimitador
Ahora no tiene que repetir la expresión regular cada vez que hace esto, simplemente diga:
fuente
Si no necesita un orden particular de valores concatenados y el separador puede ser una coma, puede hacer lo siguiente:
fuente
Necesitaba una versión DISTINTA de esto y conseguí que esta funcionara.
fuente
Un aspecto molesto
LISTAGG
es que si la longitud total de la cadena concatenada supera los 4000 caracteres (límite paraVARCHAR2
en SQL), se produce el siguiente error, que es difícil de administrar en las versiones de Oracle hasta 12.1Una nueva característica agregada en 12cR2 es la
ON OVERFLOW
cláusula deLISTAGG
. La consulta que incluye esta cláusula se vería así:Lo anterior restringirá la salida a 4000 caracteres pero no arrojará el
ORA-01489
error.Estas son algunas de las opciones adicionales de
ON OVERFLOW
cláusula:ON OVERFLOW TRUNCATE 'Contd..'
: Esto se mostrará'Contd..'
al final de la cadena (el valor predeterminado es...
)ON OVERFLOW TRUNCATE ''
: Esto mostrará los 4000 caracteres sin ninguna cadena de terminación.ON OVERFLOW TRUNCATE WITH COUNT
: Esto mostrará el número total de caracteres al final después de los caracteres finales. Por ejemplo: - '...(5512)
'ON OVERFLOW ERROR
: Si esperaLISTAGG
que falle con elORA-01489
error (que es el predeterminado de todos modos).fuente
Implementé esta función almacenada:
Lo siento, pero en algún caso (para un conjunto muy grande), Oracle podría devolver este error:
pero creo que este es un buen punto de partida;)
fuente
select col1, listaggr(col2,',') within group(Order by col2) from table group by col1
lo que significa agregar las cadenas (col2) en la lista manteniendo el orden n y luego tratar los duplicados como grupo por col1, lo que significa fusionar los duplicados col1 en 1 grupo. tal vez esto se vea limpio y simple como debería ser y si en caso de que quiera col3 también solo necesita agregar una listagg () más que esselect col1, listaggr(col2,',') within group(Order by col2),listaggr(col3,',') within group(order by col3) from table group by col1
fuente
Usar
SELECT DISTINCT ...
como parte de una subconsulta antes de llamar a LISTAGG es probablemente la mejor manera de realizar consultas simples, como lo señala @a_horse_with_no_nameSin embargo, en consultas más complejas, puede que no sea posible o fácil lograr esto. Tuve esto en un escenario que estaba usando el enfoque top-n usando una función analítica.
Entonces encontré la
COLLECT
función agregada. Está documentado que tiene el modificadorUNIQUE
oDISTINCT
disponible. Solo en 10g , falla silenciosamente (ignora el modificador sin error). Sin embargo, para superar esto, de otra respuesta , llegué a esta solución:Básicamente, al usar
SET
, elimino los duplicados de mi colección.Aún necesitaría definir
tab_typ
como un tipo de colección básico, y en el caso de aVARCHAR
, esto sería, por ejemplo:También como una corrección a la respuesta de @a_horse_with_no_name en la situación de múltiples columnas, donde es posible que desee agregar aún en una tercera (o más) columnas:
Si deja la
rn = 1
condición como where para la consulta, agregaría otras columnas incorrectamente.fuente
Muy simple: use en su consulta una subconsulta con una selección distinta:
fuente
La forma más sencilla de manejar múltiples listagg es usar 1 WITH (factor de subconsulta) por columna que contenga una listagg de esa columna de una selección distinta:
Lo que da:
fuente