Étant donné deux tables avec un nombre de lignes non défini avec un nom et une valeur, comment afficher un pivot CROSS JOIN
d'une fonction sur leurs valeurs.
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);
Par exemple, si cette fonction était une multiplication, comment générer une table (de multiplication) comme celle ci-dessous,
Toutes ces (arg1,arg2,result)
lignes peuvent être générées avec
SELECT foo.name AS arg1, bar.name AS arg2, foo.x*bar.x AS result
FROM foo
CROSS JOIN bar;
Donc, ce n'est qu'une question de présentation, je voudrais que cela fonctionne également avec un nom personnalisé - un nom qui n'est pas simplement l'argument CAST
édité dans le texte mais défini dans le tableau,
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);
Je pense que ce serait facilement faisable avec un CROSSTAB capable d'un type de retour dynamique.
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**
Mais, sans le **MAGIC**
, je reçois
ERROR: a column definition list is required for functions returning "record" LINE 1: SELECT * FROM crosstab(
Pour référence, en utilisant les exemples ci-dessus avec des noms, cela ressemble plus à ce que tablefunc
l'on crosstab()
veut.
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);
Mais, maintenant nous sommes de retour à faire des hypothèses sur le contenu et la taille de la bar
table dans notre exemple. Donc si,
- Les tables sont de longueur indéfinie,
- Ensuite, la jointure croisée représente un cube de dimension indéfinie (à cause de ci-dessus),
- Les noms de catégories (langage croisé) sont dans le tableau
Que pouvons-nous faire de mieux dans PostgreSQL sans une "liste de définitions de colonnes" pour générer ce type de présentation?
la source
Réponses:
Cas simple, SQL statique
La solution non dynamique avec
crosstab()
pour le cas simple:Je commande les colonnes résultantes par
foo.name
, nonfoo.x
. Les deux sont triés en parallèle, mais ce n'est que la configuration simple. Choisissez le bon ordre de tri pour votre cas. La valeur réelle de la deuxième colonne n'est pas pertinente dans cette requête (forme à 1 paramètre decrosstab()
).Nous n'avons même pas besoin
crosstab()
de 2 paramètres car il n'y a pas de valeurs manquantes par définition. Voir:(Vous avez corrigé la requête de tableau croisé dans la question en la remplaçant
foo
parbar
dans une modification ultérieure. Cela corrige également la requête, mais continue de travailler avec les noms defoo
.)Type de retour inconnu, SQL dynamique
Les noms et types de colonnes ne peuvent pas être dynamiques. SQL exige de connaître le nombre, les noms et les types de colonnes résultantes au moment de l'appel. Soit par déclaration explicite, soit à partir d'informations dans les catalogues système (c'est ce qui se passe avec
SELECT * FROM tbl
: Postgres recherche la définition de table enregistrée.)Vous souhaitez que Postgres dérive les colonnes résultantes des données d'une table utilisateur. Ça n'arrivera pas.
D'une manière ou d'une autre, vous avez besoin de deux aller-retour vers le serveur. Soit vous créez un curseur, puis vous le parcourez. Ou vous créez une table temporaire, puis sélectionnez-la. Ou vous enregistrez un type et l'utilisez dans l'appel.
Ou vous générez simplement la requête en une étape et l'exécutez à la suivante:
Cela génère la requête ci-dessus, dynamiquement. Exécutez-le à l'étape suivante.
J'utilise dollar-quotes (
$$
) pour simplifier la gestion des citations imbriquées. Voir:quote_ident()
est essentiel pour échapper aux noms de colonnes autrement illégaux (et éventuellement se défendre contre l'injection SQL).En relation:
la source
Si vous définissez cela comme un problème de présentation, vous pouvez envisager une fonctionnalité de présentation post-requête.
Les versions plus récentes de
psql
(9.6) sont livrées avec\crosstabview
, montrant un résultat dans la représentation croisée sans prise en charge SQL (puisque SQL ne peut pas produire cela directement, comme mentionné dans la réponse de @ Erwin: SQL exige de connaître le nombre, les noms et les types de colonnes résultantes au moment de l'appel) )Par exemple, votre première requête donne:
Le deuxième exemple avec les noms de colonnes ASCII donne:
Voir le manuel psql et https://wiki.postgresql.org/wiki/Crosstabview pour en savoir plus.
la source
Ce n'est pas une solution définitive
C'est ma meilleure approche jusqu'à présent. Encore faut-il convertir le tableau final en colonnes.
J'ai d'abord le produit cartésien des deux tables:
Mais, j'ai ajouté un numéro de ligne juste pour identifier chaque ligne de la première table.
Ensuite, j'ai construit le résultat dans ce format:
Le convertir en chaîne délimitée par des comas:
(Juste pour l'essayer plus tard: http://rextester.com/NBCYXA2183 )
la source
En guise de remarque, il semble que SQL: 2016 s'adaptera à cela avec les fonctions de tableau polymorphe (ISO / IEC TR 19075-7: 2017)
J'ai trouvé le lien Quoi de neuf dans SQL: 2016 mais l'auteur ne développe pas beaucoup sur ce point.
la source