J'ai une fonction (ou table) PostgreSQL qui me donne la sortie suivante:
Sl.no username Designation salary etc..
1 A XYZ 10000 ...
2 B RTS 50000 ...
3 C QWE 20000 ...
4 D HGD 34343 ...
Maintenant, je veux la sortie comme ci-dessous:
Sl.no 1 2 3 4 ...
Username A B C D ...
Designation XYZ RTS QWE HGD ...
Salary 10000 50000 20000 34343 ...
Comment faire ça?
Baser ma réponse sur un tableau du formulaire:
CREATE TABLE tbl (
sl_no int
, username text
, designation text
, salary int
);
Chaque ligne entraîne une nouvelle colonne à renvoyer. Avec un type de retour dynamique comme celui-ci, il n'est guère possible de le rendre complètement dynamique avec un seul appel à la base de données. Démonstration de solutions en deux étapes :
En règle générale, cela est limité par le nombre maximal de colonnes qu'une table peut contenir. Ce n'est donc pas une option pour les tableaux de plus de 1600 lignes (ou moins). Détails:
crosstab()
SELECT 'SELECT *
FROM crosstab(
''SELECT unnest(''' || quote_literal(array_agg(attname))
|| '''::text[]) AS col
, row_number() OVER ()
, unnest(ARRAY[' || string_agg(quote_ident(attname)
|| '::text', ',') || ']) AS val
FROM ' || attrelid::regclass || '
ORDER BY generate_series(1,' || count(*) || '), 2''
) t (col text, '
|| (SELECT string_agg('r'|| rn ||' text', ',')
FROM (SELECT row_number() OVER () AS rn FROM tbl) t)
|| ')' AS sql
FROM pg_attribute
WHERE attrelid = 'tbl'::regclass
AND attnum > 0
AND NOT attisdropped
GROUP BY attrelid;
Pourrait être enveloppé dans une fonction avec un seul paramètre ...
Génère une requête du formulaire:
SELECT *
FROM crosstab(
'SELECT unnest(''{sl_no,username,designation,salary}''::text[]) AS col
, row_number() OVER ()
, unnest(ARRAY[sl_no::text,username::text,designation::text,salary::text]) AS val
FROM tbl
ORDER BY generate_series(1,4), 2'
) t (col text, r1 text,r2 text,r3 text,r4 text)
Produit le résultat souhaité:
col r1 r2 r3 r4
-----------------------------------
sl_no 1 2 3 4
username A B C D
designation XYZ RTS QWE HGD
salary 10000 50000 20000 34343
unnest()
SELECT 'SELECT unnest(''{sl_no, username, designation, salary}''::text[] AS col)
, ' || string_agg('unnest('
|| quote_literal(ARRAY[sl_no::text, username::text, designation::text, salary::text])
|| '::text[]) AS row' || sl_no, E'\n , ') AS sql
FROM tbl;
Génère une requête du formulaire:
SELECT unnest('{sl_no, username, designation, salary}'::text[]) AS col
, unnest('{10,Joe,Music,1234}'::text[]) AS row1
, unnest('{11,Bob,Movie,2345}'::text[]) AS row2
, unnest('{12,Dave,Theatre,2356}'::text[]) AS row3
, unnest('{4,D,HGD,34343}'::text[]) AS row4
Même résultat.
crosstab()
Utilisez-le si vous le pouvez. Bat le reste.
SELECT 'SELECT *
FROM crosstab(
$ct$SELECT u.attnum, t.rn, u.val
FROM (SELECT row_number() OVER () AS rn, * FROM '
|| attrelid::regclass || ') t
, unnest(ARRAY[' || string_agg(quote_ident(attname)
|| '::text', ',') || '])
WITH ORDINALITY u(val, attnum)
ORDER BY 1, 2$ct$
) t (attnum bigint, '
|| (SELECT string_agg('r'|| rn ||' text', ', ')
FROM (SELECT row_number() OVER () AS rn FROM tbl) t)
|| ')' AS sql
FROM pg_attribute
WHERE attrelid = 'tbl'::regclass
AND attnum > 0
AND NOT attisdropped
GROUP BY attrelid;
Fonctionnant avec attnum
au lieu des noms de colonnes réels. Plus simple et plus rapide. Joignez le résultat à pg_attribute
Une fois de plus ou intégrez des noms de colonne comme dans l'exemple pg 9.3.
Génère une requête du formulaire:
SELECT *
FROM crosstab(
$ct$SELECT u.attnum, t.rn, u.val
FROM (SELECT row_number() OVER () AS rn, * FROM tbl) t
, unnest(ARRAY[sl_no::text,username::text,designation::text,salary::text])
WITH ORDINALITY u(val, attnum)
ORDER BY 1, 2$ct$
) t (attnum bigint, r1 text, r2 text, r3 text, r4 text);
Cela utilise toute une gamme de fonctionnalités avancées. Trop à expliquer.
unnest()
Un unnest()
peut désormais prendre plusieurs tableaux pour être imbriqués en parallèle.
SELECT 'SELECT * FROM unnest(
''{sl_no, username, designation, salary}''::text[]
, ' || string_agg(quote_literal(ARRAY[sl_no::text, username::text, designation::text, salary::text])
|| '::text[]', E'\n, ')
|| E') \n AS t(col,' || string_agg('row' || sl_no, ',') || ')' AS sql
FROM tbl;
Résultat:
SELECT * FROM unnest(
'{sl_no, username, designation, salary}'::text[]
,'{10,Joe,Music,1234}'::text[]
,'{11,Bob,Movie,2345}'::text[]
,'{12,Dave,Theatre,2356}'::text[])
AS t(col,row1,row2,row3,row4)
SQL Fiddle exécuté sur pg 9.3.
SELECT
unnest(array['Sl.no', 'username', 'Designation','salary']) AS "Columns",
unnest(array[Sl.no, username, value3Count,salary]) AS "Values"
FROM view_name
ORDER BY "Columns"
Référence: convertingColumnsToRows
Si (comme moi) vous aviez besoin de ces informations à partir d'un script bash, notez qu'il existe un simple commutateur de ligne de commande pour psql pour lui indiquer de sortir les colonnes du tableau sous forme de lignes:
psql mydbname -x -A -F= -c "SELECT * FROM foo WHERE id=123"
L'option -x
Est la clé pour que psql affiche les colonnes sous forme de lignes.
J'ai une approche plus simple que Erwin a indiqué ci-dessus, ce travailleur pour moi avec Postgres (et je pense que cela devrait fonctionner avec toutes les principales bases de données relationnelles dont le support SQL standard)
Vous pouvez utiliser simplement UNION au lieu du tableau croisé:
SELECT text 'a' AS "text" UNION SELECT 'b';
text
------
a
b
(2 rows)
Bien sûr, cela dépend du cas dans lequel vous allez appliquer cela. Étant donné que vous savez à l'avance quels champs vous avez besoin, vous pouvez adopter cette approche même pour interroger différentes tables. C'est à dire.:
SELECT 'My first metric' as name, count(*) as total from first_table UNION
SELECT 'My second metric' as name, count(*) as total from second_table
name | Total
------------------|--------
My first metric | 10
My second metric | 20
(2 rows)
C'est une approche plus maintenable, à mon humble avis. Regardez cette page pour plus d'informations: https://www.postgresql.org/docs/current/typeconv-union-case.html
Il n'existe aucun moyen approprié de le faire en SQL simple ou en PL/pgSQL.
Il sera préférable de le faire dans l'application, qui récupère les données de la base de données.