0

I was practicing SQL with PostgreSQL and I got stuck using COALESCE, maybe someone can tell me where I am going wrong.

I have a table with four columns: title, height_cm, length_cm, and width_cm. I want to multiply three last ones and get the column "size". The problem is that the whole dataset has many NULL values and 0s which I would like to skip and return the biggest value possible with the three columns (i.e. if only height has value, return that value, if the three columns have values multiply the three). If no column has a value return "NO VALUE FOUND".

I was not able to manage "skipping" the Null values (I haven't really started with the 0s yet since I am stuck in the beginning). I thought that this would have done the trick but it did not work.

 SELECT title, height_cm, length_cm, width_cm, 
        COALESCE(
        height_cm * length_cm * width_cm, 
        length_cm * width_cm, 
        height_cm * length_cm, 
        height_cm * width_cm, 
        height_cm, 
        length_cm, 
        width_cm, 'NO VALUE FOUND') AS size
FROM artworks
ORDER BY size ASC
LIMIT 10

First of all, the system does not recognize "NO VALUE FOUND" (but when I add a 0 instead, it recognizes it), secondly, when I take the text out, the system still considers the NULL values. I am able to make it work adding

WHERE height_cm IS NOT NULL AND length_cm IS NOT NULL AND width_cm IS NOT NULL

But I thought that the main idea of COALESCE was to be able to skip the NULL values. Any suggestions?

Regarding the 0s, if I add:

WHERE height_cm != 0 AND length_cm != 0 AND width_cm != 0

I lose the rows that have values but which also have one 0.

Thanks!

EDIT with the solution from the answers

In the end I used a CTE in combination with the answers from the people who helped below, this is the final query:

WITH query AS (SELECT title, height_cm, length_cm, width_cm, 
        (CASE WHEN height_cm IS NULL AND length_cm IS NULL AND width_cm IS NULL
        THEN 0
        ELSE (COALESCE(height_cm, 1) * COALESCE(length_cm, 1) * COALESCE(width_cm, 1))
        END) AS size
        FROM artworks)
        
SELECT *
FROM query
WHERE size > 0
ORDER BY size ASC
LIMIT 10

4 Answers 4

3

The datatypes of all elements passed to coalesce should be the same, but I would apply coalesce to each column separately and use the default value of 1 if it's null:

coalesce(height_cm, 1) * coalesce(length_cm, 1) * coalesce(width_cm, 1) as size

which is logically the same as what you've coded.

You'll need a special check in case they are all null, for which you may (ab)use coalesce too:

case
    when coalesce(height_cm, length_cm, width_cm) is null then null -- or 0
    else coalesce(height_cm, 1) * coalesce(length_cm, 1) * coalesce(width_cm, 1)
end as size

You'll have to pick either null or some special value (eg 0) when they are all null. You may render it as "NO VALUE FOUND" to the user as you wish.

2
  • Thanks Bohemian, I was able to make it work with 0 and using a CTE (I edit my question to show the final query). Rookie question, I wanted to filter out the rows that had 0 and in the WHERE clause I wrote size > 0 why does the query not recognize the alias "size" as a column without using a CTE? Is the order in which queries are read go from bottom to top?
    – JourneyDS
    Commented Jul 23, 2020 at 15:49
  • 1
    Most DB’s don’t allow referring a select expression alias in a where clause. Although inconvenient and it wouldn’t kill the devs to support it, makes sense because the where clause is a filter on the result rows that fires on the raw data before being rendered by the expression used for a selected value. If it were supported, performance would suffer if used due to the expression being evaluated for every row from the join result even if not in the final result rows. By forcing the coder to repeat the expression in the where clause, the potential for a performance hit is made obvious.
    – Bohemian
    Commented Jul 23, 2020 at 21:29
1

"NO VALUE FOUND" is a string type on the other hand 0 is numeric that's why when you are using 0 it does not create problem because in case of 0 all the data for this column is numeric data type but when you are trying to use string it has been created issue of mixing two different data types which will not be allowed by sql engine

1
  • Thanks Zaynul, is there a way aroyund this?
    – JourneyDS
    Commented Jul 23, 2020 at 15:41
1

I agree with Bohemian on the use of a single expression. However, I think the final expression you want returns a string:

(case when height_cm is null and length_cm is null and width_cm is null
      then 'NO VALUE FOUND'
      else (coalesce(height_cm, 1) * coalesce(length_cm, 1) * coalesce(width_cm, 1))::text
 end) as size

Or, just live with a NULL value meaning that the value is not available.

1
  • Thanks Gordon, this worked well to add the text, which is what I wanted. However, then I realized that I was not able to filter out the bottom values, this was my mistake because I actually wanted to remove those values that had a 0 from the result, so the "text" option was straightforward a bad idea on my side. In any case, your answer helped learn about the double colon "::" to change the datatype. Thanks for this!
    – JourneyDS
    Commented Jul 23, 2020 at 15:55
1

It sounds like the function you actually want to use here is GREATEST:

SELECT
    title,
    height_cm,
    length_cm,
    width_cm, 
    COALESCE(
        GREATEST(
            height_cm * length_cm * width_cm,
            length_cm * width_cm,
            height_cm * length_cm,
            height_cm * width_cm,
            height_cm,
            length_cm,
            width_cm)::text, 'NO VALUE FOUND') AS size
FROM artworks
ORDER BY size
LIMIT 10;

The GREATEST function will automatically pick up on the largest non NULL value already. So, you only need COALESCE here as a catch-all for the case where the height, length, and width, all happen to be NULL at the same time.

2
  • Hi Tim, thanks for this, is there any way around the "NO VALUE FOUND", when I put it is says: "invalid input syntax for type numeric: "NO VALUE FOUND" LINE 15: width_cm), 'NO VALUE FOUND') AS size, also, one more question doesn't GREATEST misrepresent values smaller than 0? For example 0.6 * 0.5 is 0.3 but the current query would return 0.6. Any suggestions on this?
    – JourneyDS
    Commented Jul 23, 2020 at 15:40
  • @JourneyDS Sorry, you would need to cast the arithmetic expression to text. All branches of a CASE expression must have the same type. Commented Jul 23, 2020 at 16:26

Not the answer you're looking for? Browse other questions tagged or ask your own question.