I have a table dealers in PostgreSQL. It looks like this:
insert into dealers values
('{"name":"Henry", "branches":[{"importeurs":[{"akz":"SON",number:"123456"}, {"akz":"ISA",number:"456789"}]}]}'),
('{"name":"Mike", "branches":[{"importeurs":[{"akz":"KIN",number:"133232"}, {"akz":"BAB",number:"767676"}]}]}'),
('{"name":"Sam", "branches":[{"importeurs":[{"akz":"DOM",number:"125454"}, {"akz":"QEE",number:"565665"}]}]}'),;
name column is string and branches column JSONB.
My question is: How to write a select query with given BAB and 767676 to get record Mike?
I tried this
SELECT * FROM dealers WHERE branches ->'importeurs' #> '[{"akz": "BAB", "number": "767676"}]';
I expected recode Mike back, but it returns nothing.
--------------updated--------------------
In Pic:
query returns result
query has error ERROR: operator does not exist: jsonb #? unknown
branches and importeurs are both JSON arrays, so you need to pass an array to the #> operator:
select *
from dealers
where branches -> 'branches' #> '[{"importeurs": [{"akz": "BAB", "number": "767676"}]}]';
Another option is to use a JSON path query:
where branches #? '$.branches[*].importeurs[*] ? (#.akz == "BAB" && #."number" == "767676")'
Online example
Related
I have a table with single column containing JSON type objects (column type nvarchar) and have a requirement of filtering rows.
JSON object is an array of objects containing multiple fields, Is there a way I can apply between clause on each value of array and return that row if it matches.
ROW_1 = [{"key": 12}, {"key": 13}, {"key": 19}]
For the above string if between clause has range between 15 to 22, then this row should be selected.
There are two challenges I see in above requirement, 1 is to use wild_cards to select all keys and other to apply between clause on each value.
Similar use-case, is to select a row if it matches the value for which I did something like below
select *
from table
where CAST(JSON_QUERY(column, '$'), nvarchar(max)) LIKE '%"key": 12%'
Let me know if this can be done using a T-SQL query.
PS. Other alternatives include loading all data using python and filter there. (but my concern is that I would need to load complete data every time which might slowdown the filtering due to increase in number of rows in future).
You may use an APPLY operator and an OPENJSON() call to parse the stored JSON and apply the appropriate WHERE clause:
SELECT *
FROM (VALUES (N'[{"key": 12}, {"key": 13}, {"key": 19}]')) v (JsonData)
CROSS APPLY OPENJSON(v.JsonData) WITH ([key] int '$.key') j
WHERE j.[key] BETWEEN 5 AND 12
I am storing event data in S3 and want to use Athena to query the data. One of the fields is a dynamic JSON field that I do not know the field names for. Therefore, I need to query the keys in the JSON and then use those keys to query for the first non-null for that field. Below is an example of the data stored in S3.
{
timestamp: 1558475434,
request_id: "83e21b28-7c12-11e9-8f9e-2a86e4085a59",
user_id: "example_user_id_1",
traits: {
this: "is",
dynamic: "json",
as: ["defined","by","the", "client"]
}
}
So, I need a query to extract the keys from the traits column (which is stored as JSON), and use those keys to get the first non-null value for each field.
The closest I could come was sampling a value using min_by, but this does not allow for me to add a where clause without returning null values. I will need to use presto's "first_value" option, but I cannot get this to work with the extracted JSON keys from the dynamic JSON field.
SELECT DISTINCT trait, min_by(json_extract(traits, concat('$.', cast(trait AS varchar))), received_at) AS value
FROM TABLE
CROSS JOIN UNNEST(regexp_extract_all(traits,'"([^"]+)"\s*:\s*("[^"]+"|[^,{}]+)', 1)) AS t(trait)
WHERE json_extract(traits, concat('$.', cast(trait AS varchar))) IS NOT NULL OR json_size(traits, concat('$.', cast(trait AS varchar))) <> 0
GROUP BY trait
It's not clear to me what you expect as result, and what you mean by "first non-null value". In your example you have both string and array values, and none of them is null. It would be helpful if you provided more examples and also expected output.
As a first step towards a solution, here's a way to filter out the null values from traits:
If you set the type of the traits column to map<string,string> you should be able to do something like this:
SELECT
request_id,
MAP_AGG(ARRAY_AGG(trait_key), ARRAY_AGG(trait_value)) AS trait
FROM (
SELECT
request_id,
trait_key,
trait_value
FROM some_table CROSS JOIN UNNEST (trait) AS t (trait_key, trait_value)
WHERE trait_value IS NOT NULL
)
However, if you want to also filter values that are arrays and pick out the first non-null value, that becomes more complex. It could probably be done with a combination of casts to JSON, the filter function, and COALESCE.
I have json stored [1,2,4,5].I have array s like [1,23,4]. I want to check either value of s array exist in json fields. Other options I may need to store array like ['1','2','4','5'] so i can use?| operator or their is anything else i can do ?
There is no ready-to-use function or operator to accomplish that. You can use the function:
create or replace function jsonb_int_array(jsonb)
returns integer[] language sql immutable as $function$
select array(select jsonb_array_elements_text($1)::int)
$function$;
In the query use the overlap operator:
with my_table(data) as (
values ('[1,2,4,5]'::jsonb)
)
select *
from my_table
where jsonb_int_array(data) && array[1,23,4]
data
--------------
[1, 2, 4, 5]
(1 row)
I have a postgres db with a json data field.
The json I have is an array of objects:
[{"name":"Mickey Mouse","age":10},{"name":"Donald Duck","age":5}]
I'm trying to return values for a specific key in a JSON array, so in the above example I'd like to return the values for name.
When I use the following query I just get a NULL value returned:
SELECT data->'name' AS name FROM json_test
Im assuming this is because it's an array of objects? Is it possible to directly address the name key?
Ultimately what I need to do is to return a count of every unique name, is this possible?
Thanks!
you have to unnest the array of json-objects first using the function (json_array_elements or jsonb_array_elements if you have jsonb data type), then you can access the values by specifying the key.
WITH json_test (col) AS (
values (json '[{"name":"Mickey Mouse","age":10},{"name":"Donald Duck","age":5}]')
)
SELECT
y.x->'name' "name"
FROM json_test jt,
LATERAL (SELECT json_array_elements(jt.col) x) y
-- outputs:
name
--------------
"Mickey Mouse"
"Donald Duck"
To get a count of unique names, its a similar query to the above, except the count distinct aggregate function is applied to y.x->>name
WITH json_test (col) AS (
values (json '[{"name":"Mickey Mouse","age":10},{"name":"Donald Duck","age":5}]')
)
SELECT
COUNT( DISTINCT y.x->>'name') distinct_names
FROM json_test jt,
LATERAL (SELECT json_array_elements(jt.col) x) y
It is necessary to use ->> instead of -> as the former (->>) casts the extracted value as text, which supports equality comparison (needed for distinct count), whereas the latter (->) extracts the value as json, which does not support equality comparison.
Alternatively, convert the json as jsonb and use jsonb_array_elements. JSONB supports the equality comparison, thus it is possible to use COUNT DISTINCT along with extraction via ->, i.e.
COUNT(DISTINCT (y.x::jsonb)->'name')
updated answer for postgresql versions 12+
It is now possible to extract / unnest specific keys from a list of objects using jsonb path queries, so long as the field queried is jsonb and not json.
example:
WITH json_test (col) AS (
values (jsonb '[{"name":"Mickey Mouse","age":10},{"name":"Donald Duck","age":5}]')
)
SELECT jsonb_path_query(col, '$[*].name') "name"
FROM json_test
-- replaces this original snippet:
-- SELECT
-- y.x->'name' "name"
-- FROM json_test jt,
-- LATERAL (SELECT json_array_elements(jt.col) x) y
Do like this:
SELECT * FROM json_test WHERE (column_name #> '[{"name": "Mickey Mouse"}]');
You can use jsonb_array_elements (when using jsonb) or json_array_elements (when using json) to expand the array elements.
For example:
WITH sample_data_array(arr) AS (
VALUES ('[{"name":"Mickey Mouse","age":10},{"name":"Donald Duck","age":5}]'::jsonb)
)
, sample_data_elements(elem) AS (
SELECT jsonb_array_elements(arr) FROM sample_data_array
)
SELECT elem->'name' AS extracted_name FROM sample_data_elements;
In this example, sample_data_elements is equivalent to a table with a single jsonb column called elem, with two rows (the two array elements in the initial data).
The result consists of two rows (one jsonb column, or of type text if you used ->>'name' instead):
extracted_name
----------------
"Mickey Mouse"
"Donald Duck"
(2 rows)
You should them be able to group and aggregate as usual to return the count of individual names.
My question is about searching through the contents of json arrays when searching in mysql's JSON data type.
DB Structure
So, if i have two rows in a mysql table, with a json field, called foo.
The first row has:
{
"items": [
{"type": "bar"}
]
}
The second row has:
{
"items": [
{"type": "baz"}
]
}
Things that work
I can run
select `foo`->"$.items[0].type" from `jsontest`
to return 2 results: bar and baz
I can run
select `id` from `jsontest` where `foo`->"$.items[0].type" = "bar"
to return 1 result: 1 - ie. the id of the first row.
My Problem
The mysql docs state that you can use [*] to "evaluate to the values of all elements in a JSON array".
However, the following query returns zero items:
select `id` from `jsontest` where `foo`->"$.items[*].type" = "bar"
What is wrong with my query?
Make the following query:
select id, `foo`->"$.items[*].type[0]" from `jsontest`;
You'll notice returned value is displayed as "[bar]", which is JSON array.
select * from `jsontest`
where `foo`->"$.items[*].type" = JSON_ARRAY('bar');
Anyway, the following query should work too:
select id from `jsontest` where JSON_SEARCH(`foo`, 'all','bar') is not null;
i think u want that
SELECT `id` FROM `jsontest` WHERE JSON_CONTAINS(`foo`->"$.items[*].type", '"bar"', '$')