camino correcto para crear una tabla dinámica en PostgreSQL usando CASO CUANDO
-
21-09-2019 - |
Pregunta
Estoy tratando de crear una vista de tipo de tabla dinámica en PostgreSQL y estoy casi allí! Ésta es la consulta básica:
select
acc2tax_node.acc, tax_node.name, tax_node.rank
from
tax_node, acc2tax_node
where
tax_node.taxid=acc2tax_node.taxid and acc2tax_node.acc='AJ012531';
Y los datos:
acc | name | rank
----------+-------------------------+--------------
AJ012531 | Paromalostomum fusculum | species
AJ012531 | Paromalostomum | genus
AJ012531 | Macrostomidae | family
AJ012531 | Macrostomida | order
AJ012531 | Macrostomorpha | no rank
AJ012531 | Turbellaria | class
AJ012531 | Platyhelminthes | phylum
AJ012531 | Acoelomata | no rank
AJ012531 | Bilateria | no rank
AJ012531 | Eumetazoa | no rank
AJ012531 | Metazoa | kingdom
AJ012531 | Fungi/Metazoa group | no rank
AJ012531 | Eukaryota | superkingdom
AJ012531 | cellular organisms | no rank
Lo que estoy tratando de conseguir es la siguiente:
acc | species | phylum
AJ012531 | Paromalostomum fusculum | Platyhelminthes
Estoy tratando de hacer esto con caso cuando, por lo que tengo en cuanto a lo siguiente:
select
acc2tax_node.acc,
CASE tax_node.rank WHEN 'species' THEN tax_node.name ELSE NULL END as species,
CASE tax_node.rank WHEN 'phylum' THEN tax_node.name ELSE NULL END as phylum
from
tax_node, acc2tax_node
where
tax_node.taxid=acc2tax_node.taxid and acc2tax_node.acc='AJ012531';
Lo que me da la salida:
acc | species | phylum
----------+-------------------------+-----------------
AJ012531 | Paromalostomum fusculum |
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | | Platyhelminthes
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | |
Ahora sé que tengo al grupo por el CAC en algún momento, así que intente
select
acc2tax_node.acc,
CASE tax_node.rank WHEN 'species' THEN tax_node.name ELSE NULL END as sp,
CASE tax_node.rank WHEN 'phylum' THEN tax_node.name ELSE NULL END as ph
from
tax_node, acc2tax_node
where
tax_node.taxid=acc2tax_node.taxid and acc2tax_node.acc='AJ012531'
group by acc2tax_node.acc;
Pero consigo la temida
ERROR: column "tax_node.rank" must appear in the GROUP BY clause or be used in an aggregate function
Todos los ejemplos anteriores que he sido capaz de encontrar un uso algo así como SUM () alrededor de las instrucciones CASE, así que supongo que es la función de agregado. He intentado usar primero ():
select
acc2tax_node.acc,
FIRST(CASE tax_node.rank WHEN 'species' THEN tax_node.name ELSE NULL END) as sp,
FIRST(CASE tax_node.rank WHEN 'phylum' THEN tax_node.name ELSE NULL END) as ph
from tax_node, acc2tax_node where tax_node.taxid=acc2tax_node.taxid and acc2tax_node.acc='AJ012531' group by acc2tax_node.acc;
pero obtener el error:
ERROR: function first(character varying) does not exist
¿Alguien puede ofrecer alguna pista?
Solución
Uso MAX () o MIN (), no en primer lugar (). En este escenario, tendrá todos los valores NULL en la columna por cada valor de grupo a excepción de, a lo sumo, uno con un valor no nulo. Por definición, esto es a la vez el MIN y MAX de ese conjunto de valores (todos los valores nulos se excluyen).
Otros consejos
PostgreSQL tiene un par de funciones para las consultas de pivote, consulte este artículo en Postgresonline . Puede encontrar estas funciones en el contrib .
SELECT atn.acc, ts.name AS species, tp.name AS phylum
FROM acc2tax_node atn
LEFT JOIN
tax_node ts
ON ts.taxid = atn.taxid
AND ts.rank = 'species'
LEFT JOIN
tax_node tp
ON tp.taxid = atn.taxid
AND tp.rank = 'phylum'
WHERE atn.acc = 'AJ012531 '
Más información conforme a lo solicitado (en una respuesta más que un comentario para el formato agradable):
SELECT * FROM acc2tax_node WHERE acc = 'AJ012531';
acc | taxid
----------+--------
AJ012531 | 66400
AJ012531 | 66399
AJ012531 | 39216
AJ012531 | 39215
AJ012531 | 166235
AJ012531 | 166384
AJ012531 | 6157
AJ012531 | 33214
AJ012531 | 33213
AJ012531 | 6072
AJ012531 | 33208
AJ012531 | 33154
AJ012531 | 2759
AJ012531 | 131567
Ejecutar:
SELECT report.* FROM crosstab(
select
acc2tax_node.acc, tax_node.name, tax_node.rank
from
tax_node, acc2tax_node
where
tax_node.taxid=acc2tax_node.taxid and acc2tax_node.acc='AJ012531';
) AS report(species text, enus text, family text, ...)
Como Matthew Wood señaló, utilizar MIN () o MAX (), no en primer lugar ():
SELECT
an.acc,
MAX(
CASE tn.rank
WHEN 'species' THEN tn.name
ELSE NULL
END
) AS species,
MAX(
CASE tn.rank
WHEN 'phylum' THEN tn.name
ELSE NULL
END
) AS phylum
FROM tax_node tn,
acc2tax_node an
WHERE tn.taxid = an.taxid
and an.acc = 'AJ012531'
GROUP by an.acc;