Home > OS >  Error "more than one row returned by a subquery used as an expression"
Error "more than one row returned by a subquery used as an expression"

Time:09-20

I have the following 3 tables:

airport

airport_id  name
1           Frankfurt
2           Paris
3           Amsterdam

area

areaid    name    airport_id
1         name1   2
2         name2   2
3         name3   3
4         name4   3

booking

id      booking_date        price   commission  areaid
1       2022-09-1T10:00     70      12          1
2       2022-09-2T11:00     60      16          2
3       2022-09-2T20:00     50      15          3
4       2022-09-3T01:00     110     15          3
5       2022-09-10T22:00    90      14          4
6       2022-09-11T19:00    65      12          1
7       2022-09-20T12:00    84      16          2

And I have this query

SELECT ar.name,
     (SELECT (b.price * b.commission) AS com
        FROM booking AS b
        LEFT JOIN area AS p ON b.areaid = p.areaid
        AND p.areaid = 3
        AND (b.booking_date >= '2022-09-01T00:00' AND b.booking_date <= '2022-09-30T23:59:59')
      )
FROM airport AS ar WHERE ar.airport_id = 2 

Running the query I get the error:

more than one row returned by a subquery used as an expression

I don't understand what the problem is.

CodePudding user response:

The query

SELECT (b.price * b.commission) AS com
    FROM booking AS b
    LEFT JOIN area AS p ON b.areaid = p.areaid
    AND p.areaid = 3
    AND (b.booking_date >= '2022-09-01T00:00' AND b.booking_date <= '2022-09-30T23:59:59')

returns 7 rows. A subquery used in the SELECT list must return no more than 1 row.

A natural way to get what you want is to join all tables, e.g.:

SELECT
    ar.name,
    (b.price * b.commission) AS com
FROM airport AS ar
LEFT JOIN area AS p 
    ON ar.airport_id = p.airport_id
LEFT JOIN booking AS b 
    ON b.areaid = p.areaid
    AND p.areaid = 3
    AND b.booking_date >= '2022-09-01T00:00' 
    AND b.booking_date <= '2022-09-30T23:59:59'

Replace LEFT JOIN with JOIN to skip rows with null results.

CodePudding user response:

A subquery in the SELECT list is only allowed to return a single value. Not multiple rows, nor multiple columns. If you want any of those, the standard replacement is a LATERAL subquery. See:

The more severe problem with your query is that it doesn't make sense at all.

This might be what you want, returning the list of all commissions (if any) for a given airport and a given area:

SELECT ar.name AS airport, b.com
FROM   airport ar
LEFT   JOIN LATERAL (
   SELECT round(b.price * b.commission / 100.0) AS com
   FROM   area    p
   JOIN   booking b USING (areaid)
   WHERE  p.airport_id = ar.airport_id  -- my assumption
   AND    b.areaid = 3
   AND    b.booking_date >= '2022-09-01'
   AND    b.booking_date <  '2022-10-01'
   ) b ON true
WHERE  ar.airport_id = 2;

Your subquery was uncorrelated. Assuming you really meant to link to the given airport via airport_id.

LEFT JOIN area AS p made no sense in combination with the condition WHERE p.areaid = 3. That's a hidden [INNER] JOIN. See:

The filter b.booking_date <= '2022-09-30T23:59:59'ma be slightly incorrect, too (unless your data is guaranteed to have a 1-minute resolution). Either way, b.booking_date < '2022-10-01' is the proper way to include "all of September". '2022-10-01' is a valid timestamp literal, short for '2022-10-01T00:00:00'.

Assuming the value in commission is really meant to be a percentage.

  • Related