2010-03-19 14 views
6

Próbuję utworzyć widok typu tabela przestawna w postgresql i jestem prawie tam! Oto podstawowego zapytania:poprawny sposób tworzenia tabeli przestawnej w postgresql przy użyciu 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'; 

a dane:

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 

Próbuję dostać się następująco:

acc  | species     | phylum 
AJ012531 | Paromalostomum fusculum | Platyhelminthes 

Staram się robić to z przypadku, gdy , więc mam aż o:

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

Który daje mi wynik:

acc |   species   |  phylum  
----------+-------------------------+----------------- 
AJ012531 | Paromalostomum fusculum | 
AJ012531 |       | 
AJ012531 |       | 
AJ012531 |       | 
AJ012531 |       | 
AJ012531 |       | 
AJ012531 |       | Platyhelminthes 
AJ012531 |       | 
AJ012531 |       | 
AJ012531 |       | 
AJ012531 |       | 
AJ012531 |       | 
AJ012531 |       | 
AJ012531 |       | 

Teraz wiem, że mam do grupy przez acc w pewnym momencie, więc staram

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; 

Ale pojawia się bał

ERROR: column "tax_node.rank" must appear in the GROUP BY clause or be used in an aggregate function 

Wszystkie poprzednie przykłady, które udało mi się znaleźć, używają czegoś takiego jak SUM() wokół instrukcji CASE, więc domyślam się, że jest to funkcja agregująca. Próbowałem przy użyciu 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; 

ale pojawia się błąd:

ERROR: function first(character varying) does not exist 

Może ktoś oferują żadnych wskazówek?

+0

mógłbyś dodawać wyniki tej kwerendy: ' SELECT * FROM acc2tax_node WHERE acc = 'AJ012531''? – Quassnoi

Odpowiedz

5

Użyj przycisku MAX() lub MIN(), a nie pierwszego(). W tym scenariuszu wszystkie kolumny NULL będą w kolumnie dla każdej wartości grupy, z wyjątkiem co najwyżej jednej z wartością niezerową. Z definicji jest to MIN i MAX tego zestawu wartości (wszystkie wartości zerowe są wykluczone).

+0

To działa idealnie, dziękuję. Z jakiegoś powodu założyłem, że funkcja MAX() nie będzie działać, ponieważ korzystałem z wartości łańcuchowych. – 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

Proszę wybaczyć moje przerażające próby formatowania :-) – mojones

0

Więcej informacji na żądanie (w odpowiedzi zamiast komentarz dla ładnej formatowania):

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 ma kilka funkcji do zapytań przestawnych, zobacz ten artykuł na Postgresonline. Możesz znaleźć te funkcje w contrib.

+0

Tak, podejrzewam, że poprawny sposób to zrobić z scrosstab. Nadal chciałbym dowiedzieć się, co robię źle tutaj dla mojej własnej edukacji. – 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

Jak Matthew Wood wskazał, należy użyć MIN() lub MAX(), nie 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;