views:

119

answers:

4

I'm trying construct a PostgreSQL query that does the following but so far my efforts have been in vain.

Problem: There are two tables: A and B. I'd like to select all columns from table A (having columns: id, name, description) and substitute the "A.name" column with the value of the column "B.title" from table B (having columns: id, table_A_id title, langcode) where B.table_A_id is 5 and B.langcode is "nl" (if there are any rows).

My attempts:

SELECT A.name,
 case when exists(select title from B where table_A_id = 5 and langcode= 'nl')
 then B.title
 else A.name
END
FROM A, B
WHERE A.id = 5 and B.table_A_id = 5 and B.langcode = 'nl'

-- second try:
SELECT COALESCE(B.title, A.name) as name
from A, B
where A.id = 5 and B.table_A_id = 5 and exists(select title from B where table_A_id = 5 and langcode= 'nl')

I've tried using a CASE and COALESCE() but failed due to my inexperience with both concepts.

Thanks in advance.

A: 

Try this

select A.id,B.title,A.description from TableA as A inner join tableB as B
on A.id=B.id where B.table_A_id = 5 and B.langcode ='nl'
Madhivanan
This query won't return A.name when B.title is non-existing.
EarthMind
+1  A: 

Ok, I'm not sure how your tables have to be joined, but something like this should do the job:

SELECT            yourcolumnlist,
                  CASE WHEN A.name IS NULL THEN B.title ELSE A.name END
FROM              TableA AS A
INNER JOIN        TableB AS B
ON                A.id = B.table_A_id
WHERE             B.table_A_id = 5
AND               B.langcode = 'nl'

Another way to do it would be to use the COALESCE() function:

SELECT            yourcolumnlist,
                  COALESCE(A.name, B.title)
FROM              TableA AS A
INNER JOIN        TableB AS B
ON                A.id = B.table_A_id
WHERE             B.table_A_id = 5
AND               B.langcode = 'nl'
Maximilian Mayerl
Both queries return no rows. I have to point out that when table B doesn't have rows where langcode = 'nl', it should still return all rows from table A. And if there are rows where langcode = 'nl' then the field should be substituted as mentioned in my first post.
EarthMind
+2  A: 
select A.id, coalesce(B.title, A.name)
from TableA AS A
     left join (select table_a_id, title from TableB where langcode = 'nl') AS B
       on B.table_a_id = A.id
WHERE A.id = 5
araqnid
This is what you want, I guess. The only caveat is that this can potentially give more that one row per TableA row, if there are more than one TableB row that matches.
leonbloy
This also doesn't behave as wanted. It returns A.name every time, regardless of whether B.title exists or not.
EarthMind
+1 This should work and, in general, is better than my answer.
leonbloy
+2  A: 

araqnid's is the answer you are looking for, I bet.

But if you want to enforce that no more than one row is returned for each original matching A row, you might prefer to do a subselect instead of a LEFT JOIN. For example:

SELECT A.id, COALESCE(
  ( SELECT max(B.title) FROM B WHERE
    langcode = 'nl' AND B.table_a_id = A.id), A.name ) as name
FROM  A
WHERE A.id = 5

I use "max" here to select an arbitrary value, in the event there is more than one. You can use "min" or whatever you consider appropiate in your case.

Perhaps this is more easy to understand than the LEFT JOIN, but (apart from the two being not exactly equivalent) a JOIN will perform better than N subselects (much better is N is large).

Anyway, from a learning point of view, it's good to understand both.

leonbloy
This does exactly what I want. It substitutes A.name with B.title if the second exists, otherwise it uses A.name as return value from the COALESCE function. The B table will always only have 1 row for the given WHERE condition but I wonder why you used MAX() (or MIN()) instead of LIMIT 1. Also, just out of curiosity, is there a way to replace the name column with the value from the COALESCE() function? I use A.* to get all the values from table A and alias the result from COALESCE() as name and it adds an additional column name1 in the returned array. Thanks again.
EarthMind
You can perfectly use `LIMIT 1` (it's just a little more Postgresql-centric than MAX/MIN). I added an alias ("name"). I'm afraid that you'll have to replace your `A.*` with the full columns list (except `name`). Anyway, using `A.*` is considered bad practice, usually.
leonbloy