Reputation: 2201
I have two tables: goods with column id and cost and good_texts with columns goodid, languageid, title. So good_texts contains translations for good.
I have query that selects title for current language if it available and selects default title if it is not available for one good:
SELECT
*
FROM
good_texts
WHERE
languageid = 2
UNION ALL
SELECT
*
FROM
good_texts
WHERE
languageid = 1 AND goodid = 92
AND goodid NOT IN (
SELECT
goodid
FROM
good_texts
WHERE
languageid = 2 and goodid = 92
)
This works fine and returns only one row. Now I'm trying to write query to get all data from good table as well as title from good_texts table. I'm trying to do this using this query:
SELECT
s1.id,
s1.cost,
(SELECT
s2.title
FROM
good_texts s2
WHERE
languageid = 2
UNION ALL
SELECT
s3.title
FROM
good_texts s3
WHERE
languageid = 1 AND s3.goodid = s1.id
AND s3.goodid NOT IN (
SELECT
s4.goodid
FROM
good_texts s4
WHERE
languageid = 2 AND s4.goodid = s1.id
))
FROM
goods s1
But this doesn't work with error
more than one row returned by a subquery used as an expression
I can't understand why my subquery returns more than one row even so it returns one row if it runs separately. How my goal can be achieved?
Upvotes: 0
Views: 7126
Reputation: 2201
I've managed to resolve the issue using @Gordon Linoff code and final result was:
SELECT gt.*, goods.*
FROM good_texts gt
JOIN goods ON goods.id = gt.goodid
WHERE gt.languageid = 2
UNION ALL
SELECT gt.*, goods.*
FROM good_texts gt
JOIN goods ON goods.id = gt.goodid
WHERE gt.languageid = 1 AND
NOT EXISTS (SELECT 1
FROM good_texts gt2
WHERE gt2.goodid = gt.goodid AND gt2.languageid = 2
)
Upvotes: 0
Reputation: 1269933
The query that you want looks like this:
SELECT gt.*
FROM good_texts gt
WHERE gt.languageid = 2
UNION ALL
SELECT gt.*
FROM good_texts gt
WHERE gt.languageid = 1 AND
NOT EXISTS (SELECT 1
FROM good_texts gt2
WHERE gt2.goodid = gt.goodid AND gt2.languageid = 2
);
The first gets everything with language id "2". The second gets all goods who have language id "1" but not "2".
In Postgres, this is more easily done using DISTINCT ON
:
SELECT DISTINCT ON (gt.goodid) gt.*
FROM good_texts gt
WHERE gt.languageid IN (1, 2)
ORDER BY gt.goodid, (CASE WHEN gt.languageid = 2 THEN 1 ELSE 2 END);
Upvotes: 3
Reputation: 2642
The select statement expects to operate over one row of data. The from and where clauses will determine how many rows of data the query returns (other things can as well but in an effort to keep it simple we can ignore those here).
Basically you are asking the database to select something and then in one of the fields in your select statement you are trying to again select something that returns more than 1 row, which causes the error.
You need to change your logic to use joins instead of sub selects.
On mobile so can't provide code examples right now.
Upvotes: 0
Reputation: 133370
The condition are not the same
in working one query is
AND goodid NOT IN (
SELECT
goodid
FROM
good_texts
WHERE
languageid = 2 and goodid = 92
)
and in the not working one is
AND title NOT IN (
SELECT
title
FROM
good_texts s4
WHERE
languageid = 2 AND s4.goodid = s1.id
)
You select different column goodid
and title
in subquery in NOT in clause ..
Upvotes: 0