правильный способ создания сводной таблицы в postgresql, используя случай, когда
Я пытаюсь создать представление типа сводной таблицы в postgresql и почти там! Вот основной запрос:
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';
и данные:
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
то, что я пытаюсь сделать, это следующее:
acc | species | phylum
AJ012531 | Paromalostomum fusculum | Platyhelminthes
Я пытаюсь сделать это с CASE WHEN, поэтому у меня есть следующее:
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';
что дает мне выход:
acc | species | phylum
----------+-------------------------+-----------------
AJ012531 | Paromalostomum fusculum |
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | | Platyhelminthes
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | |
AJ012531 | |
теперь я знаю, что мне нужно сгруппироваться по acc в какой-то момент, поэтому я пытаюсь
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;
но я получаю страшный
ERROR: column "tax_node.rank" must appear in the GROUP BY clause or be used in an aggregate function
все предыдущие примеры, которые я смог найти, используют что-то вроде SUM() вокруг операторов CASE, поэтому я думаю, что это агрегатная функция. Я попытался использовать FIRST ():
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;
но получаю ошибку:
ERROR: function first(character varying) does not exist
может ли кто-нибудь предложить какие-либо подсказки?
6 ответов
используйте MAX () или MIN (), а не FIRST (). В этом случае все нули в столбце для каждого значения группы, за исключением, самое большее, один с not null значение. По определению, это MIN и MAX этого набора значений (все нули исключены).
PostgreSQL имеет несколько функций для сводных запросов, см. Эту статью в Postgresonline. Вы можете найти эти функции в 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 '
дополнительная информация по запросу (в ответе, а не в комментарии для хорошего форматирования):
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
выполнить:
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, ...)
Как указал Мэтью Вуд, используйте MIN () или MAX (), а не FIRST ():
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;