Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use Values from JSONB Array inside a WHERE IN Clause

I have a JSONB object in PostgreSQL:

'{"cars": ["bmw", "mercedes", "pinto"], "user_name": "ed"}'

I am trying to use values from the "cars" array inside it in the WHERE clause of a SELECT:

SELECT car_id FROM cars WHERE car_type IN ('bmw', 'mercedes', 'pinto');

This will correctly return the values 1, 2, and 3 - see table setup at the bottom of this post.

Currently, in my function I do this:

(1) Extract the "cars" array into a variable `v_car_results`.
(2) Use that variable in the `WHERE` clause.

Pseudo code:

    DECLARE v_car_results TEXT
    BEGIN
        v_car_results = '{"cars": ["bmw", "mercedes", "pinto"], "user_name": "ed"}'::json#>>'{cars}';
            -- this returns 'bmw', 'mercedes', 'pinto'
        SELECT car_id FROM cars WHERE car_type IN ( v_car_results );
    END

However, the SELECT statement is not returning any rows. I know it's reading those 3 car types as a single type. (If I only include one car_type in the "cars" element, the query works fine.)

How would I treat these values as an array inside the WHERE clause?

I've tried a few other things:

  1. The ANY clause.

  2. Various attempts at casting.

  3. These queries:

    SELECT car_id FROM cars
    WHERE car_type IN (json_array_elements_text('["bmw", "mercedes", "pinto"]'));
    
    ...
    WHERE car_type IN ('{"cars": ["bmw", "mercedes", "pinto"], "user_name": "ed"}':json->>'cars');
    

It feels like it's something simple I'm missing. But I've fallen down the rabbit hole on this one. (Maybe I shouldn't even be using the ::json#>> operator?)

TABLE SETUP

CREATE TABLE cars (
   car_id SMALLINT
 , car_type VARCHAR(255)
);

INSERT INTO cars (car_id, car_type)
VALUES
  (1, 'bmw')
, (2, 'mercedes')
, (3, 'pinto')
, (4, 'corolla');

SELECT car_id FROM cars
WHERE car_type IN ('bmw', 'mercedes', 'pinto'); -- Returns Values : 1, 2, 3
like image 773
bjones1831 Avatar asked Jul 03 '16 23:07

bjones1831


People also ask

How do I query a Jsonb column?

To query JSON data, you can use standard T-SQL. If you must create a query or report on JSON data, you can easily convert JSON data to rows and columns by calling the OPENJSON rowset function. For more information, see Convert JSON Data to Rows and Columns with OPENJSON (SQL Server).

Is Jsonb faster than JSON?

Because JSONB stores data in a binary format, queries process significantly faster. Storing data in binary form allows Postgres to access a particular JSON key-value pair without reading the entire JSON record. The reduced disk load speeds up overall performance.

Can Jsonb be an array?

jsonb elements can be of the following types: Objects. Arrays. String.


1 Answers

Assuming the current Postgres version 9.5 since it wasn't specified.

Use the set-returning function jsonb_array_elements_text() (as table function!) and join to the result:

SELECT c.car_id
FROM   jsonb_array_elements_text('{"cars": ["bmw", "mercedes", "pinto"]
                                 , "user_name": "ed"}'::jsonb->'cars') t(car_type)
JOIN   cars c USING (car_type);

Extract the JSON array from the object with jsonb->'cars' and pass the resulting JSON array (still data type jsonb) to the function. (The operator #> would do the job as well.)

Aside: ::json#>> isn't just an operator. It's a cast to json (::json), followed by the operator #>>. You don't need either.

The resulting type text conveniently matches your column type varchar(255), so we don't need type-casting. And assign the column name car_type to allow for the syntax shorthand with USING in the join condition.

This form is shorter, more elegant and typically a bit faster than alternatives with IN () or = ANY() - which would work too. Your attempts were pretty close, but you need the variants with a subquery. This would work:

SELECT car_id FROM cars
WHERE  car_type IN (SELECT json_array_elements_text('["bmw", "mercedes", "pinto"]'));

Or, cleaner:

SELECT car_id FROM cars
WHERE  car_type IN (SELECT * FROM json_array_elements_text('["bmw", "mercedes", "pinto"]'));

Detailed explanation:

  • How to use ANY instead of IN in a WHERE clause with Rails?

Related:

  • How to turn json array into postgres array?
like image 100
Erwin Brandstetter Avatar answered Oct 17 '22 02:10

Erwin Brandstetter