sergeda
sergeda

Reputation: 2201

SQL subquery combine with UNION ALL query

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

Answers (4)

sergeda
sergeda

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

Gordon Linoff
Gordon Linoff

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

thomas
thomas

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

ScaisEdge
ScaisEdge

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

Related Questions