Pergunta

dados

Eu desigualmente distribuídas (data wrt) por alguns anos (2003-2008). Quero dados de consulta para um determinado conjunto de início e data final, que agrupa os dados por qualquer um dos intervalos suportados (dia, semana, mês, trimestre, ano) em PostgreSQL 8.3 ( http://www.postgresql.org/docs/8.3/static/functions-datetime.html # FUNÇÕES-DATETIME-TRUNC ).

O problema é que algumas das consultas dar resultados contínuos durante o período necessário, como este:

select to_char(date_trunc('month',date), 'YYYY-MM-DD'),count(distinct post_id) 
from some_table where category_id=1 and entity_id = 77  and entity2_id = 115 
and date <= '2008-12-06' and date >= '2007-12-01' group by 
date_trunc('month',date) order by date_trunc('month',date);
          to_char   | count 
        ------------+-------
         2007-12-01 |    64
         2008-01-01 |    31
         2008-02-01 |    14
         2008-03-01 |    21
         2008-04-01 |    28
         2008-05-01 |    44
         2008-06-01 |   100
         2008-07-01 |    72
         2008-08-01 |    91
         2008-09-01 |    92
         2008-10-01 |    79
         2008-11-01 |    65
        (12 rows)

mas alguns deles perca alguns intervalos, porque não há dados presentes, como este:

select to_char(date_trunc('month',date), 'YYYY-MM-DD'),count(distinct post_id) 
from some_table where category_id=1 and entity_id = 75  and entity2_id = 115 
and date <= '2008-12-06' and date >= '2007-12-01' group by 
date_trunc('month',date) order by date_trunc('month',date);

        to_char   | count 
    ------------+-------

     2007-12-01 |     2
     2008-01-01 |     2
     2008-03-01 |     1
     2008-04-01 |     2
     2008-06-01 |     1
     2008-08-01 |     3
     2008-10-01 |     2
    (7 rows)

, onde o conjunto de resultados necessária é:

  to_char   | count 
------------+-------
 2007-12-01 |     2
 2008-01-01 |     2
 2008-02-01 |     0
 2008-03-01 |     1
 2008-04-01 |     2
 2008-05-01 |     0
 2008-06-01 |     1
 2008-07-01 |     0
 2008-08-01 |     3
 2008-09-01 |     0
 2008-10-01 |     2
 2008-11-01 |     0
(12 rows)

A contagem de 0 para entradas em falta.

Eu vi discussões anteriores sobre estouro de pilha, mas eles não resolver o meu problema que parece, desde o meu período de agrupamento é um dos (dia, semana, mês, trimestre, ano) e decidiu, em tempo de execução pelo aplicativo. Assim, uma abordagem como LEFT JOIN com uma mesa de calendário ou tabela de seqüência não vai ajudar, eu acho.

Minha solução atual para isso é para preencher essas lacunas em Python (em um Turbogears App), utilizando o módulo de calendário.

Existe uma maneira melhor de fazer isso.

Foi útil?

Solução

Você pode criar uma lista de todos os primeiros dias do último ano (digamos) com

select distinct date_trunc('month', (current_date - offs)) as date 
from generate_series(0,365,28) as offs;
          date
------------------------
 2007-12-01 00:00:00+01
 2008-01-01 00:00:00+01
 2008-02-01 00:00:00+01
 2008-03-01 00:00:00+01
 2008-04-01 00:00:00+02
 2008-05-01 00:00:00+02
 2008-06-01 00:00:00+02
 2008-07-01 00:00:00+02
 2008-08-01 00:00:00+02
 2008-09-01 00:00:00+02
 2008-10-01 00:00:00+02
 2008-11-01 00:00:00+01
 2008-12-01 00:00:00+01

Em seguida, você pode se juntar com essa série.

Outras dicas

Esta questão é velho. Mas desde que outros usuários escolheu-o como mestre para um novo duplicar Estou adicionando uma resposta adequada.

solução adequada

SELECT *
FROM  (
   SELECT day::date
   FROM   generate_series(timestamp '2007-12-01'
                        , timestamp '2008-12-01'
                        , interval  '1 month') day
   ) d
LEFT   JOIN (
   SELECT date_trunc('month', date_col)::date AS day
        , count(*) AS some_count
   FROM   tbl
   WHERE  date_col >= date '2007-12-01'
   AND    date_col <= date '2008-12-06'
-- AND    ... more conditions
   GROUP  BY 1
   ) t USING (day)
ORDER  BY day;
  • Use LEFT JOIN, é claro.

  • generate_series() pode produzir uma tabela de timestamps on the fly, e muito rápido.

  • Em geral, é mais rápido para agregados antes que você participar. Recentemente forneceu um caso de teste em sqlfiddle.com nesta resposta relacionada:

  • Lançai a timestamp para date (::date) para um formato básico. Para mais uso to_char() .

  • GROUP BY 1 é sintaxe abreviada para fazer referência a primeira coluna de saída. Poderia ser GROUP BY day bem, mas que o conflito poder com uma coluna existente de mesmo nome. Ou GROUP BY date_trunc('month', date_col)::date mas isso é demasiado longo para o meu gosto.

  • Funciona com os argumentos de intervalo disponíveis para date_trunc() .

  • count() nunca produz NULL (0 para nenhuma linha), mas a LEFT JOIN faz.
    Para retornar 0 vez de NULL na SELECT exterior, o uso COALESCE(some_count, 0) AS some_count. O manual.

  • Para uma solução mais genérico ou intervalos de tempo arbitrários considerar esta resposta intimamente relacionados:

Você pode criar uma tabela temporária em tempo de execução e esquerda juntar-se sobre isso. Que parece fazer mais sentido.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top