Assuming I have below table structure:
And address_collection has a value of this per table row entry:
How would I be able to query a specific json object from the array based on an address_uid assigned to user_id?
JSON_EXTRACT() and its -> alias doesn't work (returns null) because as I understand, it only works on a single JSON Object.
Example:
SELECT
u.*
FROM
user u
WHERE
u.user_id = 1
AND
u.address_collection->"$.address_uid" = "a83b662f-60de-4d3d-ab05-43b0746fb2a2";
Above example yields no result due to u.address_collection->"$.address_uid" = "a83b662f-60de-4d3d-ab05-43b0746fb2a2";
Modifying that line to u.address_collection->"$[0].address_uid" = "a83b662f-60de-4d3d-ab05-43b0746fb2a2"; doesn't work either as the result of the left part returns a collection of address_uid from the object array instead of searching it.
Related
I have a JSON datatype with array values in a MySQL database as highlighted in the picture below:
I want to compare multiple values with this category field, Ex: ["49","27"].
How to write a MySQL query for this?
I tried this query:
SELECT l.*, pc.name as cat_name,u.name as uname
FROM listing l
LEFT OUTER JOIN package_purchased_history ph ON ph.user_id = l.user_id AND ph.expired_date >= 1656095400 AND ph.purchase_date <= 1656095400
LEFT OUTER JOIN user u ON u.id = l.user_id
INNER JOIN category pc ON JSON_SEARCH(l.categories, 'one', pc.id) AND pc.parent = 26
WHERE JSON_CONTAINS(l.categories,'["49"]','$[0]') IS NOT NULL
AND l.status = 'active'
GROUP BY l.id
Unfortunately it is not working, so please suggest me the a better approach.
$[0] is the first element of the array, not the whole array. So you're testing whether an array is contained in a single number, not whether the array is contained in the array.
The whole array is $, since that refers to the top-level element of the JSON value. But you don't need to specify the path when you're searching the whole value.
WHERE JSON_CONTAINS(l.categories, '[49]')
You don't need IS NOT NULL there, since JSON_CONTAINS() returns a boolean. The value will never be NULL unless l.categories is NULL.
I am writing a SQL query and I have an array in one table and in that array I stores IDs and want to compare that array of IDs with another table to show data against that ids. when I run it gives me following error.
No database selected Select the default DB to be used by double-clicking.
Here is my query
select TagId
, Name
from ctrData2.Tag
Left
outer join ctrData2.CallDetail
On Tag.TagId = array(CallDetail.Tag)
where CallDetail.ContactId = 'f9d4787a-f1ac-41af-97d8-ea324daad018'
this is how I store IDs in array in Tag Column
here is the other table from where I want to show data against these ids
you can set the default schema by below query first
use ctrData2;
And run below query
select TagId, Name
from Tag, CallDetail
where CallDetail.ContactId = 'f9d4787a-f1ac-41af-97d8-ea324daad018' and LOCATE(Tag.TagId, CallDetail.Tag) > 0;
I have the following code:
SELECT stores_tb.stores, sum(products_tb_tb.prices)
from products_tb
inner join stores_tb
on products_tb.id_store = stores_tb.id_store
where products_tb.barcode IN ($barcodes)
group by stores_tb.stores
order by sum(products_tb.prices)
Being the $barcodes an array (already converted to a string) that I receive via ajax in a php file that executes the MySQL.
The thing is that the IN is inclusive, using OR for each of the array values, meaning that if one of the stores required on the SELECT have one, but not all of the barcodes in the array, it will be shown.
I wanna know if there is a function like the IN (or a way to use the IN function) in which it will return only the stores that have all of the barcodes passed in the array, the equvilant of using AND instead of OR for each of the array values.
You can do this with a having clause:
select s.stores, sum(p.prices)
from products_tb p join
stores_tb s
on p.id_store = s.id_store
where p.barcode IN ($barcodes)
group by s.stores
having count(distinct p.barcode) = $n -- the number of codes that need to match
order by sum(p.prices);
The $n value is the length of the $barcodes list (strictly speaking, the number of unique items in it).
Instead of an array and an IN clause You could use a subselect and the ALL operator
SELECT stores_tb.stores, sum(products_tb_tb.prices)
from products_tb
inner join stores_tb
on products_tb.id_store = stores_tb.id_store
where products_tb.barcode = ALL (
select barcode from my_table )
)
group by stores_tb.stores
order by sum(products_tb.prices)
https://dev.mysql.com/doc/refman/5.7/en/any-in-some-subqueries.html
https://dev.mysql.com/doc/refman/5.7/en/all-subqueries.html
I have a JSON column, manifest, containing an array of objects.
I need to return all table rows where any of the objects in their array have a slide_id that is present in a sub select.
The structure of the JSON field is..
{ matrix:[
{
row:1,
col:1,
slide_id:1
},
{
row:1,
col:2,
slide_id:5
}
]
}
So I want to run something like this....
SELECT id FROM presentation WHERE manifest->'$.matrix[*].slide_id' IN ( (SELECT id from slides WHERE date_deleted IS NOT NULL) );
But this doesn't work as manifest->'$.matrix[*].slide_id' returns a JSON array for each row.
I have managed to get this to work, but its amazingly slow as it scans the whole table...
SELECT
p.id
FROM
(
SELECT id,
manifest->'$.matrix[*].slide_id' as slide_ids
FROM `presentation`
) p
INNER JOIN `pp_slides` s
ON JSON_CONTAINS(p.slide_ids, CAST(s.id as json), '$')
WHERE s.date_deleted IS NOT NULL
If I filter it down to an individual presentation ID, then its not too bad, but still takes 700 ms for a presentation with a couple of hundred slides in it. Is there a cleaner way to do this?
I suppose the best way would be to refactor it to store the matrix as a relational table....
Datamodel
A person is represented in the database as a meta table row with a name and with multiple attributes which are stored in the data table as key-value pair (key and value are in separate columns).
Simplified data-model
Now there is a query to retrieve all users (name) with all their attributes (data). The attributes are returned as JSON object in a separate column. Here is an example:
name data
Florian { "age":25 }
Markus { "age":25, "color":"blue" }
Thomas {}
The SQL command looks like this:
SELECT
name,
json_object_agg(d.key, d.value) AS data,
FROM meta AS m
JOIN (
JOIN d.fk_id, d.key, d.value AS value FROM data AS d
) AS d
ON d.fk_id = m.id
GROUP BY m.name;
Problem
Now the problem I am facing is, that users like Thomas which do not have any attributes stored in the key-value table, are not shown with my select function. This is because it does only a JOIN and no LEFT OUTER JOIN.
If I would use LEFT OUTER JOIN then I run into the problem, that json_object_agg try's to aggregate NULL values and dies with an error.
Approaches
1. Return empty list of keys and values
So I tried to check if the key-column of a user is NULL and return an empty array so json_object_agg would just create an empty JSON object.
But there is not really a function to create an empty array in SQL. The nearest thing I found was this:
select '{}'::text[];
In combination with COALESCE the query looks like this:
json_object_agg(COALESCE(d.key, '{}'::text[]), COALESCE(d.value, '{}'::text[])) AS data
But if I try to use this I get following error:
ERROR: COALESCE types text and text[] cannot be matched
LINE 10: json_object_agg(COALESCE(d.key, '{}'::text[]), COALES...
^
Query failed
PostgreSQL said: COALESCE types text and text[] cannot be matched
So it looks like that at runtime d.key is a single value and not an array.
2. Split up JSON creation and return empty list
So I tried to take json_object_agg and replace it with json_object which does not aggregate the keys for me:
json_object(COALESCE(array_agg(d.key), '{}'::text[]), COALESCE(array_agg(d.value), '{}'::text[])) AS data
But there I get the error that null value not allowed for object key. So COALESCE does not check that the array is empty.
Qustion
So, is there a function to check if a joined column is empty, and if yes return just a simple JSON object?
Or is there any other solution which would solve my problem?
Use left join with coalesce(). As default value use '{}'::json.
select name, coalesce(d.data, '{}'::json) as data
from meta m
left join (
select fk_id, json_object_agg(d.key, d.value) as data
from data d
group by 1
) d
on m.id = d.fk_id;
name | data
---------+------------------------------------
Florian | { "age" : "25" }
Marcus | { "age" : "25", "color" : "blue" }
Thomas | {}
(3 rows)