postgresql retorna 0 se o valor retornado for nulo

99

Tenho uma consulta que retorna média (preço)

  select avg(price)
  from(
      select *, cume_dist() OVER (ORDER BY price desc) from web_price_scan
      where listing_Type='AARM'
        and u_kbalikepartnumbers_id = 1000307
        and (EXTRACT(Day FROM (Now()-dateEnded)))*24 < 48
        and price>( select avg(price)* 0.50
                    from(select *, cume_dist() OVER (ORDER BY price desc)
                         from web_price_scan
                         where listing_Type='AARM'
                           and u_kbalikepartnumbers_id = 1000307
                           and (EXTRACT(Day FROM (Now()-dateEnded)))*24 < 48
                        )g
                   where cume_dist < 0.50
                 )
        and price<( select avg(price)*2
                    from( select *, cume_dist() OVER (ORDER BY price desc)
                          from web_price_scan
                          where listing_Type='AARM'
                            and u_kbalikepartnumbers_id = 1000307
                            and (EXTRACT(Day FROM (Now()-dateEnded)))*24 < 48
                        )d
                    where cume_dist < 0.50)
     )s

  having count(*) > 5

como fazê-lo retornar 0 se nenhum valor estiver disponível?

Andrew
fonte
1
Tem certeza de que sua consulta está bem formada?
Luc M
2
@LucM: Não pode ser uma consulta bem formada. (cláusula "tendo" sem uma cláusula "agrupar por".)
Mike Sherrill 'Cat Recall'
tudo funciona bem, exceto que às vezes, quando as regras não são cumpridas, ele não retorna nada. Além disso, como posso aumentar em média, acho que não é possível || qual é o ponto? Várias seleções from web_price_scansão seleções separadas; não tenho certeza qual é o problema aqui?
Andrew
Não há problema em usar uma havingcláusula sem um group by(que o padrão é um único grupo). Ele atua como uma wherecláusula sobre resultados agregados. Nesse caso, as linhas são retornadas apenas se mais de 5 linhas forem retornadas pela subconsulta de primeiro nível.
bruceskyaus de

Respostas:

177

usar coalescer

COALESCE(value [, ...])
The COALESCE function returns the first of its arguments that is not null.  
Null is returned only if all arguments are null. It is often
used to substitute a default value for null values when data is
retrieved for display.

Editar

Aqui está um exemplo de COALESCEcom sua consulta:

SELECT AVG( price )
FROM(
      SELECT *, cume_dist() OVER ( ORDER BY price DESC ) FROM web_price_scan
      WHERE listing_Type = 'AARM'
        AND u_kbalikepartnumbers_id = 1000307
        AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
        AND COALESCE( price, 0 ) > ( SELECT AVG( COALESCE( price, 0 ) )* 0.50
                                     FROM ( SELECT *, cume_dist() OVER ( ORDER BY price DESC )
                                           FROM web_price_scan
                                           WHERE listing_Type='AARM'
                                             AND u_kbalikepartnumbers_id = 1000307
                                             AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
                                         ) g
                                    WHERE cume_dist < 0.50
                                  )
        AND COALESCE( price, 0 ) < ( SELECT AVG( COALESCE( price, 0 ) ) *2
                                     FROM( SELECT *, cume_dist() OVER ( ORDER BY price desc )
                                           FROM web_price_scan
                                           WHERE listing_Type='AARM'
                                             AND u_kbalikepartnumbers_id = 1000307
                                             AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
                                         ) d
                                     WHERE cume_dist < 0.50)
     )s
HAVING COUNT(*) > 5

IMHO COALESCEnão deve ser usado com AVGporque modifica o valor. NULLsignifica desconhecido e nada mais. Não é como usá-lo SUM. Neste exemplo, se substituirmos AVGpor SUM, o resultado não será distorcido. Adicionar 0 a uma soma não prejudica ninguém, mas calculando uma média com 0 para os valores desconhecidos, você não obtém a média real.

Nesse caso, gostaria de acrescentar price IS NOT NULLna WHEREcláusula para evitar esses valores desconhecidos.

Luc M
fonte
1
@Andrew, eu estava tentando dar um exemplo usando sua consulta. Mas eu me perco. Duvido que essa consulta funcione. from web_price_scan...parece repetido ...
Luc M
Para quem está se perguntando, NULLIF(v1, v2)faz praticamente o oposto de COALESCE, pois retorna NULLse for v1igual v2.
sm
24

(esta resposta foi adicionada para fornecer exemplos mais curtos e genéricos para a pergunta - sem incluir todos os detalhes específicos do caso na pergunta original).


Existem dois "problemas" distintos aqui, o primeiro é se uma tabela ou subconsulta não tem linhas, o segundo é se há valores NULL na consulta.

Para todas as versões que testei, postgres e mysql irão ignorar todos os valores NULL ao calcular a média e retornará NULL se não houver nada para calcular a média. Isso geralmente faz sentido, pois NULL deve ser considerado "desconhecido". Se você quiser sobrescrever isso, você pode usar coalescer (como sugerido por Luc M).

$ create table foo (bar int);
CREATE TABLE

$ select avg(bar) from foo;
 avg 
-----

(1 row)

$ select coalesce(avg(bar), 0) from foo;
 coalesce 
----------
        0
(1 row)

$ insert into foo values (3);
INSERT 0 1
$ insert into foo values (9);
INSERT 0 1
$ insert into foo values (NULL);
INSERT 0 1
$ select coalesce(avg(bar), 0) from foo;
      coalesce      
--------------------
 6.0000000000000000
(1 row)

claro, "de foo" pode ser substituído por "de (... qualquer lógica complicada aqui ...) como foo"

Agora, a linha NULL na tabela deve ser contada como 0? Em seguida, coalescer deve ser usado dentro da chamada avg.

$ select coalesce(avg(coalesce(bar, 0)), 0) from foo;
      coalesce      
--------------------
 4.0000000000000000
(1 row)
Tobixen
fonte
2

Posso pensar em 2 maneiras de fazer isso:

  • IFNULL ():

    A função IFNULL () retorna um valor especificado se a expressão for NULL. Se a expressão for NOT NULL, esta função retornará a expressão.

Sintaxe:

IFNULL(expression, alt_value)

Exemplo de IFNULL () com sua consulta:

SELECT AVG( price )
FROM(
      SELECT *, cume_dist() OVER ( ORDER BY price DESC ) FROM web_price_scan
      WHERE listing_Type = 'AARM'
        AND u_kbalikepartnumbers_id = 1000307
        AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
        AND IFNULL( price, 0 ) > ( SELECT AVG( IFNULL( price, 0 ) )* 0.50
                                     FROM ( SELECT *, cume_dist() OVER ( ORDER BY price DESC )
                                           FROM web_price_scan
                                           WHERE listing_Type='AARM'
                                             AND u_kbalikepartnumbers_id = 1000307
                                             AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
                                         ) g
                                    WHERE cume_dist < 0.50
                                  )
        AND IFNULL( price, 0 ) < ( SELECT AVG( IFNULL( price, 0 ) ) *2
                                     FROM( SELECT *, cume_dist() OVER ( ORDER BY price desc )
                                           FROM web_price_scan
                                           WHERE listing_Type='AARM'
                                             AND u_kbalikepartnumbers_id = 1000307
                                             AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
                                         ) d
                                     WHERE cume_dist < 0.50)
     )s
HAVING COUNT(*) > 5
  • COALESCE ()

    A função COALESCE () retorna o primeiro valor não nulo em uma lista.

Sintaxe:

COALESCE(val1, val2, ...., val_n)

Exemplo de COALESCE () com sua consulta:

SELECT AVG( price )
FROM(
      SELECT *, cume_dist() OVER ( ORDER BY price DESC ) FROM web_price_scan
      WHERE listing_Type = 'AARM'
        AND u_kbalikepartnumbers_id = 1000307
        AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
        AND COALESCE( price, 0 ) > ( SELECT AVG( COALESCE( price, 0 ) )* 0.50
                                     FROM ( SELECT *, cume_dist() OVER ( ORDER BY price DESC )
                                           FROM web_price_scan
                                           WHERE listing_Type='AARM'
                                             AND u_kbalikepartnumbers_id = 1000307
                                             AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
                                         ) g
                                    WHERE cume_dist < 0.50
                                  )
        AND COALESCE( price, 0 ) < ( SELECT AVG( COALESCE( price, 0 ) ) *2
                                     FROM( SELECT *, cume_dist() OVER ( ORDER BY price desc )
                                           FROM web_price_scan
                                           WHERE listing_Type='AARM'
                                             AND u_kbalikepartnumbers_id = 1000307
                                             AND ( EXTRACT( DAY FROM ( NOW() - dateEnded ) ) ) * 24 < 48
                                         ) d
                                     WHERE cume_dist < 0.50)
     )s
HAVING COUNT(*) > 5
Joish
fonte
1
IFNULL () não é uma função do Postgres. Isso pode funcionar em outros bancos de dados, mas a questão é especificamente sobre o Postgres.
Jon Wilson,