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:
The ANY
clause.
Various attempts at casting.
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?)
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
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).
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.
jsonb elements can be of the following types: Objects. Arrays. String.
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:
Related:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With