Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return null if a join has no match, in conjunction with json_build_object?

I've got two tables, one with a foreign key to the other:

CREATE TABLE employee (
  employee_id INT PRIMARY KEY,
  name TEXT NOT NULL
);

CREATE TABLE project (
  project_id INT PRIMARY KEY,
  employee_id INT NOT NULL REFERENCES employee(employee_id),
  name text NOT NULL
);

I'm trying to query for an employee, and their project (assume only one project is allowed). I'd like the project to appear as a JSON object, if a project exists, otherwise I'd ideally like null.

I've got the following query, which does what I'd like ...

SELECT e.employee_id,
       e.name,
       row_to_json(p.*) AS project
FROM employee e
LEFT JOIN project p USING(employee_id)

Problem is, I want to selectively choose the columns from the join, so I've adjusted the query to be as follows:

SELECT e.employee_id,
       e.name,
       json_build_object('name', p.name) AS project
FROM employee e
LEFT JOIN project p USING(employee_id)

The problem with the above query is that if an employee has no matching project, the json_build_object still produces an object with null values, as opposed to just not producing an object. The row_to_json query is able to see that the join has no matching row and so it creates no json object.

Is there any way to achieve what I'm looking for, while being able to build my own JSON object using the columns that I choose?

like image 677
Ryan Avatar asked Dec 25 '15 02:12

Ryan


People also ask

Why are my join results not showing null values?

If the key column in either A or B contains NULL, that row will never appear in join results Thanks for this this detailed answer! Found the answer. The reason this was not working is because I was using the AND condition on the JOIN and not in the WHEREclause.

Does join return non matching rows in SQL?

The JOIN or INNER JOIN does not return any non-matching rows at all. It returns only the rows that match in both of the tables you join. If you want to get any unmatched rows, you shouldn’t use it.

What does a full join query return?

This FULL JOIN query will return the following data: Let’s inspect the data a bit. There are rows that are found in both tables. There are, however, rows from the table employee that have no corresponding rows in the table project. You can identify these rows by the NULL values in the column project_name.

How to replace null values with something else in MySQL?

The results are the following: As you can see, we are missing a row.   The row containing the NULL values did not get picked up by our join.  We should have all three rows in the result set.   The join statement does not deal with NULL values well when joining.   So we can use ISNULL to replace the NULL values with something else.


2 Answers

You can use CASE to examine if the value is null:

SELECT e.employee_id,
       e.name,
       CASE 
            WHEN p.name IS NULL THEN NULL
            ELSE json_build_object('name', p.name) 
       END AS project
FROM employee e
LEFT JOIN project p USING(employee_id);

This function can be useful if you intend to repeatedly select different values:

create or replace function to_json_or_null(key text, val text)
returns json language sql as $$
    select case
        when val is null then null
        else json_build_object(key, val)
    end
$$;

SELECT e.employee_id,
       e.name,
       to_json_or_null('name', p.name) AS project
FROM employee e
LEFT JOIN project p USING(employee_id);
like image 155
klin Avatar answered Nov 01 '22 14:11

klin


Use LEFT JOIN LATERAL and build your custom object in the subquery.

SELECT e.employee_id,
       e.name,
       p2.project
FROM employee e
LEFT JOIN LATERAL (
    SELECT json_build_object('name', p.name) AS project
    FROM project AS p
    WHERE p.employee_id = e.id
) p2 ON true
like image 34
David Spiess Avatar answered Nov 01 '22 13:11

David Spiess