Dadas dos tablas con un recuento de filas indefinido con un nombre y un valor, ¿cómo mostraría CROSS JOIN
una función pivotada sobre sus valores?
CREATE TEMP TABLE foo AS
SELECT x::text AS name, x::int
FROM generate_series(1,10) AS t(x);
CREATE TEMP TABLE bar AS
SELECT x::text AS name, x::int
FROM generate_series(1,5) AS t(x);
Por ejemplo, si esa función fuera la multiplicación, ¿cómo generaría una tabla (de multiplicación) como la siguiente?
Todas esas (arg1,arg2,result)
filas se pueden generar con
SELECT foo.name AS arg1, bar.name AS arg2, foo.x*bar.x AS result
FROM foo
CROSS JOIN bar;
Entonces, esto es solo una cuestión de presentación, me gustaría que esto también funcione con un nombre personalizado , un nombre que no es simplemente el argumento CAST
editado en texto, sino que se establece en la tabla,
CREATE TEMP TABLE foo AS
SELECT chr(x+64) AS name, x::int
FROM generate_series(1,10) AS t(x);
CREATE TEMP TABLE bar AS
SELECT chr(x+72) AS name, x::int
FROM generate_series(1,5) AS t(x);
Creo que esto sería fácilmente posible con un CROSSTAB capaz de un tipo de retorno dinámico.
SELECT * FROM crosstab(
'
SELECT foo.x AS arg1, bar.x AS arg2, foo.x*bar.x
FROM foo
CROSS JOIN bar
', 'SELECT DISTINCT name FROM bar'
) AS **MAGIC**
Pero, sin el **MAGIC**
, me sale
ERROR: a column definition list is required for functions returning "record" LINE 1: SELECT * FROM crosstab(
A modo de referencia, utilizando los ejemplos anteriores, con nombres que esto es algo más parecido a lo que tablefunc
's crosstab()
necesidades.
SELECT * FROM crosstab(
'
SELECT foo.x AS arg1, bar.x AS arg2, foo.x*bar.x
FROM foo
CROSS JOIN bar
'
) AS t(row int, i int, j int, k int, l int, m int);
Pero, ahora volvemos a hacer suposiciones sobre el contenido y el tamaño de la bar
tabla en nuestro ejemplo. Así que si,
- Las tablas son de longitud indefinida,
- Entonces la unión cruzada representa un cubo de dimensión indefinida (debido a lo anterior),
- Los nombres de categoría (lenguaje de tabla cruzada) están en la tabla
¿Qué es lo mejor que podemos hacer en PostgreSQL sin una "lista de definición de columnas" para generar ese tipo de presentación?
fuente
Respuestas:
Caso simple, SQL estático
La solución no dinámica con
crosstab()
para el caso simple:Ordeno las columnas resultantes por
foo.name
, nofoo.x
. Ambos están ordenados en paralelo, pero esa es solo la configuración simple. Elija el orden de clasificación adecuado para su caso. El valor real de la segunda columna es irrelevante en esta consulta (forma de 1 parámetrocrosstab()
).Ni siquiera necesitamos
crosstab()
2 parámetros porque, por definición, no faltan valores. Ver:(Arreglaste la consulta de tabla cruzada en la pregunta reemplazándola
foo
porbar
una edición posterior. Esto también corrige la consulta, pero sigue trabajando con nombres defoo
).Tipo de retorno desconocido, SQL dinámico
Los nombres y tipos de columna no pueden ser dinámicos. SQL exige saber el número, los nombres y los tipos de columnas resultantes en el momento de la llamada. Ya sea por declaración explícita o por información en los catálogos del sistema (Eso es lo que sucede con
SELECT * FROM tbl
: Postgres busca la definición de tabla registrada).Desea que Postgres derive las columnas resultantes de los datos en una tabla de usuario. No va a pasar.
De una forma u otra, necesita dos viajes de ida y vuelta al servidor. O creas un cursor y luego lo recorres. O puede crear una tabla temporal y luego seleccionarla. O registra un tipo y lo usa en la llamada.
O simplemente genera la consulta en un paso y la ejecuta en el siguiente:
Esto genera la consulta anterior, dinámicamente. Ejecútelo en el siguiente paso.
Estoy usando dollar-quotes (
$$
) para simplificar el manejo de las cotizaciones anidadas. Ver:quote_ident()
es esencial para escapar de nombres de columna ilegales (y posiblemente defenderse contra la inyección de SQL).Relacionado:
fuente
Si enmarca esto como un problema de presentación, podría considerar una función de presentación posterior a la consulta.
psql
Vienen versiones más nuevas de (9.6)\crosstabview
, que muestran un resultado en la representación de tabla cruzada sin soporte de SQL (ya que SQL no puede producir esto directamente, como se menciona en la respuesta de @ Erwin: SQL exige saber el número, los nombres y los tipos de columnas resultantes en el momento de la llamada )Por ejemplo, su primera consulta da:
El segundo ejemplo con nombres de columna ASCII da:
Consulte el manual de psql y https://wiki.postgresql.org/wiki/Crosstabview para obtener más información.
fuente
Esta no es una solución definitiva
Este es mi mejor enfoque hasta ahora. Todavía necesita convertir la matriz final en columnas.
Primero tengo el producto cartesiano de ambas tablas:
Pero, agregué un número de fila solo para identificar cada fila de la primera tabla.
Luego he compilado el resultado en este formato:
Convirtiéndolo en una cadena delimitada por comas:
(Solo para probarlo más tarde: http://rextester.com/NBCYXA2183 )
fuente
Como nota al margen, parece que SQL: 2016 acomodará esto con funciones de tabla polimórficas (ISO / IEC TR 19075-7: 2017)
Encontré el enlace Novedades en SQL: 2016 pero el autor no se expande tanto.
fuente