2010-03-19 1 views
6

Я пытаюсь создать вид типа сводной таблицы в postgresql и почти там! Вот основной вопрос:правильный способ создания сводной таблицы в postgresql с использованием CASE WHEN

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 

Я пытаюсь сделать это с случай, когда , поэтому я получил следующее:

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 |       | 

Теперь я знаю, что я должен группе акк в какой-то момент, поэтому я стараюсь

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 

Может кто-нибудь предложить какие-то намеки?

+0

Не могли бы вы опубликовать результаты этого запроса: ' SELECT * FROM acc2tax_node WHERE acc = 'AJ012531''? – Quassnoi

ответ

5

Используйте MAX() или MIN(), а не FIRST(). В этом случае у вас будут все NULL в столбце на каждое групповое значение, за исключением, самое большее, одного с непустым значением. По определению это как MIN, так и MAX этого набора значений (исключаются все NULL).

+0

Это прекрасно работает, спасибо. По какой-то причине я предположил, что MAX() не будет работать, потому что я использовал строковые значения. – mojones

0
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 ' 
+0

Прошу прощения за мои ужасные попытки форматирования :-) – mojones

0

Дополнительная информация по запросу (в ответе, а не комментарий к красивому форматированию):

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 
2

PostgreSQL имеет несколько функций для шарнирных запросов, обратитесь к этой статье на Postgresonline. Эти функции можно найти в contrib.

+0

Да, я подозреваю, что правильный способ сделать это с помощью scrosstab. Мне все равно хотелось бы выяснить, что я делаю неправильно здесь для моего собственного образования. – mojones

0

Execute:

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, ...) 
0

Как Мэтью Вуд отметил, использовать MIN() или MAX(), а не первый():

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; 
Смежные вопросы