postgresql zwraca 0, jeśli zwrócona wartość jest równa null

101

Mam zapytanie, które zwraca średnią (cenę)

  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

jak sprawić, by zwracał 0, jeśli żadna wartość nie jest dostępna?

Andrzej
źródło
1
Czy na pewno Twoje zapytanie jest dobrze sformułowane?
Luc M
2
@LucM: To nie może być dobrze sformułowane zapytanie. (klauzula „posiadanie” bez klauzuli „grupa by”).
Mike Sherrill „Cat Recall”
wszystko działa dobrze, z wyjątkiem tego, że czasami, gdy zasady nie są spełnione, nic nie zwraca. Poza tym, jak mogę przeciętnie rozgryźć, nie sądzę, że to możliwe || o co chodzi? Wielokrotne wybory from web_price_scanto oddzielne wybory; nie wiesz, o co tu chodzi?
Andrew
Dobrze jest użyć havingklauzuli bez group by(co domyślnie dotyczy pojedynczej grupy). Działa jako whereklauzula dotycząca wyników zagregowanych. W takim przypadku wiersze są zwracane tylko wtedy, gdy podzapytanie pierwszego poziomu zwraca więcej niż 5 wierszy.
bruceskyaus

Odpowiedzi:

180

użyj coalesce

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.

Edytować

Oto przykład COALESCEzapytania:

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

COALESCENie należy używać IMHO z, AVGponieważ modyfikuje wartość. NULLoznacza nieznane i nic więcej. To nie jest jak używanie go w SUM. W tym przykładzie, jeśli zastąpimy AVGprzez SUM, wynik nie jest zniekształcony. Dodanie 0 do sumy nikomu nie zaszkodzi, ale obliczenie średniej z 0 dla nieznanych wartości, nie otrzymasz prawdziwej średniej.

W takim przypadku dodałbym price IS NOT NULLw WHEREklauzuli, aby uniknąć tych nieznanych wartości.

Luc M
źródło
1
@ Andrew Próbowałem podać przykład za pomocą Twojego zapytania. Ale się zgubiłem. Wątpię, czy to zapytanie działa. from web_price_scan...wydaje się powtarzać ...
Luc M
Dla tych, którzy się zastanawiają, NULLIF(v1, v2)robi coś zupełnie przeciwnego COALESCE, ponieważ zwraca, NULLjeśli v1jest równe v2.
sm
24

(ta odpowiedź została dodana, aby przedstawić krótsze i bardziej ogólne przykłady pytania - bez uwzględniania wszystkich szczegółów dotyczących konkretnego przypadku w pierwotnym pytaniu).


Występują tutaj dwa różne „problemy”, pierwszy dotyczy sytuacji, w której tabela lub podzapytanie nie ma wierszy, a drugi dotyczy wartości NULL w zapytaniu.

We wszystkich testowanych wersjach postgres i mysql ignorują wszystkie wartości NULL podczas uśredniania i zwracają NULL, jeśli nie ma nic do uśrednienia. Generalnie ma to sens, ponieważ NULL należy uważać za „nieznane”. Jeśli chcesz to zmienić, możesz użyć coalesce (zgodnie z sugestią Luca 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)

oczywiście "from foo" można zastąpić "from (... tutaj dowolna skomplikowana logika ...) as foo"

Czy teraz wiersz NULL w tabeli powinien być liczony jako 0? Następnie coalesce musi być użyte w wywołaniu avg.

$ select coalesce(avg(coalesce(bar, 0)), 0) from foo;
      coalesce      
--------------------
 4.0000000000000000
(1 row)
tobixen
źródło
2

Przychodzą mi do głowy 2 sposoby, aby to osiągnąć:

  • IFNULL ():

    Funkcja IFNULL () zwraca określoną wartość, jeśli wyrażenie ma wartość NULL. Jeśli wyrażenie NIE ma wartości NULL, ta funkcja zwraca wyrażenie.

Składnia:

IFNULL(expression, alt_value)

Przykład IFNULL () w zapytaniu:

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
  • ŁĄCZYĆ()

    Funkcja COALESCE () zwraca pierwszą wartość różną od null na liście.

Składnia:

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

Przykład COALESCE () z zapytaniem:

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
źródło
1
IFNULL () nie jest funkcją w Postgres. Może to działać w innych bazach danych, ale pytanie dotyczy szczególnie Postgres.
Jon Wilson,