I have this json data in a 'name' column, where the names of some records are stored with multi-lang support:
{"en":"Professional Association","de":"Berufsverband","uk":null}
When I run this query returns 0 records:
select * from `some_table` where lower(json_value(name,'$.*')) like lower('%Berufsverband%');
But if I run the query specifying the lang key (de) it works:
select * from `some_table` where lower(json_value(name,'$.de')) like lower('%Berufsverband%');
My question is, how can I properly use the $.* wildcard on MariaDB?
If I run the exact same query on MySQL it works fine:
select * from `some_table` where lower(json_unquote(name->'$.*')) like lower('%Berufsverband%');
You can use a combination of JSON_CONTAINS and JSON_EXTRACT.
You cannot use wildcards within JSON_CONTAINS, but you can within JSON_EXTRACT, so, if you wrap the contains with an extract using a wild card (see Object Member Selector in JSONPath Expression below) you will return the rows containing the value you're searching for.
Object Member Selector
To select member(s) in a JSON object, one can use one of the
following:
.memberName selects the value of the member with name memberName.
."memberName" - the same as above but allows one to select a member with a name that's not a valid identifier (that is, has
space, dot, and/or other characters)
.* - selects the values of all members of the object.
select * from `some_table`
WHERE JSON_CONTAINS(JSON_EXTRACT(name, "$.*"), '"Berufsverband"')
db<>fiddle here.
I just made it work, I replaced json_value with json_query. The result was something like this:
select * from `some_table` where lower(json_query (name, '$')) like lower('%Berufsverband%');
Now it works as expected.
(This is an extension to this question, but my reputation is too low to comment or ask more questions on that topic...)
We work on bigquery, hence limited in importing packages or using other languages. And, as per the link above, js is a solution, but not what I'm looking for here. I implemented it in js, and it was too slow for our needs.
Suppose one of our columns is a string that look like this (array of json):
[{"location":[22.99902,66.000],"t":1},{"location":[55.32168,140.556],"t":2},{"location":[85.0002,20.0055],"t":3}]
I want to extract from the column the json for which "t":2
Where:
some columns don't have elements "t":2
Some columns have several elements "t":2
The number of json elements in each string can change
element "t":2 is not always in second position.
I don't know regexp well enough for this. We tried regexp_extract with this pattern: r'(\{.*?\"t\":2.*?\})')), but that doesn't work. It extracts everything that precedes "t":2, including the json for "t":2. We only want the json of element "t":2.
Could you advise a regexp pattern that would work?
EDIT:
I have a preference for a solution that gives me 1 match. Suppose I have this string:
[{"location":[22.99902,66.000],"t":1},{"location":[55.32168,140.556],"t":2},{"location":[55.33,141.785],"t":2}],
I would prefer receiving only 1 answer, the first one.
In that case perhaps regexp is less appropriate, but I'm really not sure?
How about this:
(?<=\{)(?=.*?\"t\"\s*:\s*2).*?(?=\})
As seen here
There is another solution but it is not regexp based (as I had originally asked). So this should not count as the final answer to my own question, nonetheless could be useful.
It is based on a split of the string in array and then chosing the element in the array that satisfies my needs.
Steps:
transform the string into something better for splits (using '|' as seperator):
replace(replace(replace(my_field,'},{','}|{'),'[{','{'),'}]','}')
split it using split(), which yields an array of strings (each one a json element)
find the relevant element ("t":2) - in my case, the first one is good enough, so I limit the query to 1: array( select data from unnest(split(replace(replace(replace(my_field,'},{','}|{'),'[{','{'),'}]','}'),'|')) as data where data like '%"t":2%' limit 1)
Convert that into a useable string with array_to_string() and use json_extract on that string to extract the relevant info from the element that I need (say for example, location coordinate x).
So putting it all together:
round(safe_cast(json_extract(array_to_string(array( select data from unnest(split(replace(replace(replace(my_field,'},{','}|{'),'[{','{'),'}]','}'),'|')) as data where data like '%"t":2%' limit 1),''),'$.location[0]') as float64),3) loc_x
May 1st, 2020 Update
A new function, JSON_EXTRACT_ARRAY, has been just added to the list of JSON
functions. This function allows you to extract the contents of a JSON document as
a string array.
so in below you can replace use of json2array UDF with just in-built function JSON_EXTRACT_ARRAY as in below example
#standardSQL
SELECT id,
(
SELECT x
FROM UNNEST(JSON_EXTRACT_ARRAY(json, '$')) x
WHERE JSON_EXTRACT_SCALAR(x, '$.t') = '2'
) extracted
FROM `project.dataset.table`
==============
Below is for BigQuery Standard SQL
#standardSQL
CREATE TEMP FUNCTION json2array(json STRING)
RETURNS ARRAY<STRING>
LANGUAGE js AS """
return JSON.parse(json).map(x=>JSON.stringify(x));
""";
SELECT id,
(
SELECT x
FROM UNNEST(json2array(JSON_EXTRACT(json, '$'))) x
WHERE JSON_EXTRACT_SCALAR(x, '$.t') = '2'
) extracted
FROM `project.dataset.table`
You can test, play with above using dummy data as in below example
#standardSQL
CREATE TEMP FUNCTION json2array(json STRING)
RETURNS ARRAY<STRING>
LANGUAGE js AS """
return JSON.parse(json).map(x=>JSON.stringify(x));
""";
WITH `project.dataset.table` AS (
SELECT 1 id, '[{"location":[22.99902,66.000],"t":1},{"location":[55.32168,140.556],"t":2},{"location":[85.0002,20.0055],"t":3}]' json UNION ALL
SELECT 2, '[{"location":[22.99902,66.000],"t":11},{"location":[85.0002,20.0055],"t":13}]'
)
SELECT id,
(
SELECT x
FROM UNNEST(json2array(JSON_EXTRACT(json, '$'))) x
WHERE JSON_EXTRACT_SCALAR(x, '$.t') = '2'
) extracted
FROM `project.dataset.table`
with output
Row id extracted
1 1 {"location":[55.32168,140.556],"t":2}
2 2 null
Above assumes that there is no more than one element with "t":2 in json column. In case if there can be more than one - you should add ARRAY as below
SELECT id,
ARRAY(
SELECT x
FROM UNNEST(json2array(JSON_EXTRACT(json, '$'))) x
WHERE JSON_EXTRACT_SCALAR(x, '$.t') = '2'
) extracted
FROM `project.dataset.table`
Even though, you have posted a work around your issue. I believe this answer will be informative. You mentioned that one of the answer selected more than what you needed, I wrote the query below to reproduce your case and achieve aimed output.
WITH
data AS (
SELECT
" [{ \"location\":[22.99902,66.000]\"t\":1},{\"location\":[55.32168,140.556],\"t\":2},{\"location\":[85.0002,20.0055],\"t\":3}] " AS string_j
UNION ALL
SELECT
" [{ \"location\":[22.99902,66.000]\"t\":1},{\"location\":[55.32168,140.556],\"t\":3},{\"location\":[85.0002,20.0055],\"t\":3}] " AS string_j
UNION ALL
SELECT
" [{ \"location\":[22.99902,66.000]\"t\":1},{\"location\":[55.32168,140.556],\"t\":3},{\"location\":[85.0002,20.0055],\"t\":3}] " AS string_j
UNION ALL
SELECT
" [{ \"location\":[22.99902,66.000]\"t\":1},{\"location\":[55.32168,140.556],\"t\":3},{\"location\":[85.0002,20.0055],\"t\":3}] " AS string_j ),
refined_data AS (
SELECT
REGEXP_EXTRACT(string_j, r"\{\"\w*\"\:\[\d*\.\d*\,\d*\.\d*\]\,\"t\"\:2\}") AS desired_field
FROM
data )
SELECT
*
FROM
refined_data
WHERE
desired_field IS NOT NULL
Notice that I have used the dummy described in the temp table, populated inside the WITH method. As below:
Afterwords, in the table refined_data, I used the REGEXP_EXTRACT to extract the desired string from the column. Observe that for the rows which there is not a match expression, the output is null. Thus, the table refined_data is as follows :
As you can see, now it is just needed a simple WHERE filter to obtain the desired output, which was done in the last select.
In addition you can see the information about the regex expression I provided here.
I have two fields in my MySQL database that are arrays. The datatype is shown as LONGTEXT with a Comment of (DC2Type:array).
For example, the integer values stored in this field would look like this:
a:4:{i:0;i:9;i:1;i:10;i:2;i:11;i:3;i:12;}
And the String values would look like this:
a:2:{i:0;s:6:"Value1";i:1;s:6:"Value2";}
I need these fields this way so I can store columns that are filterable. E.g. the first one may be age groups so ages 9,10,11,12 are represented.
My query must then get all records that say are relevant for age 10 or in some cases say I want to find those that are 10 and 11.
I've tried the IN and FIND_IN_SET syntaxes but neither is returning any results.
Using IN
SELECT *
FROM table_name
WHERE MyField IN (10)
Using FIND_IN_SET
SELECT *
FROM table_name
WHERE FIND_IN_SET(MyField,'Value1') > 0;
I know arrays are probably not the best field to store values in but I didn't want to have separate fields for each AgeGroup e.g. Age1, Age2, etc. or each category e.g Value1, Value2, etc.
Any thoughts on how I can find a value or values from a database array field, please?
Thanks!
You can use a pattern match.
Integer:
WHERE MyField LIKE '%i:10;%'
String:
WHERE MyField LIKE '%s:6:"Value1";%'
6 has to be replaced with the length of the string you're searching for.
If you want to search for multiple numbers or strings, you can use a regular expression with alternation:
WHERE MyField RLIKE 'i:(10|11);'
WHERE MyField RLIKE 's:(6:"Value1"|10:"LongValue2");'
Note that none of these methods can make use of an index on the table. It's generally a bad idea to store arrays in database columns, you should store them as separate rows in a many-to-many table.
I am accessing an array (a json object called 'choice_values') in a jsonb field, and would like to parse its contents into a comma-separated text field.
SELECT
jsonb_array_elements(doc -> 'form_values' -> '8189' -> 'choice_values')
FROM
field_data.exports;
That jsonb_array_elements function returns a "setof text", which I would like converted to a comma separated list of the array values, contained within a single field.
Thank you.
Set returning functions (like jsonb_array_elements_text()) can be called in SELECT list, but then they cannot be used in aggregate functions.
This is a good practice to call set returning functions in FROM clause, often in a lateral join like in this example:
with the_data as (
select '["alfa", "beta", "gamma"]'::jsonb as js
)
select string_agg(elem, ',')
from
the_data,
jsonb_array_elements_text(js) elem;
string_agg
-----------------
alfa,beta,gamma
(1 row)
So your query should look like this:
select string_agg(elem, ',')
from
field_data.exports,
jsonb_array_elements_text(doc -> 'form_values' -> '8189' -> 'choice_values') elem;
Using the string_agg aggregate function with a sub-select from jsonb_array_elements_text seems to work (tested on PG 9.5). Note the use of jsonb_array_elements_text, added in PostgreSQL 9.4, rather than jsonb_array_elements, from PostgreSQL 9.3.
with exports as (
select $${"form_values": {"8189": {"choice_values": ["a","b","c"]}}}$$::jsonb as doc
)
SELECT
string_agg(values, ', ')
FROM
exports, jsonb_array_elements_text(doc -> 'form_values' -> '8189' -> 'choice_values') values
GROUP BY
exports.doc;
Output:
'a, b, c'
Also see this question and its answers.
Maybe not best practice: convert the json array to text, then remove the brackets.
WITH input AS (
SELECT '["text1","text2","text3"]'::jsonb as data
)
SELECT substring(data::text,2,length(data::text)-2) FROM input
It has the advantage that it converts "in-place", not by aggregating. This could be handy if you can only access part of the query, e.g. for some synchronization tool where there's field-based conversion rules, or something like the following:
CREATE TEMP TABLE example AS (SELECT '["text1","text2","text3"]'::jsonb as data);
ALTER TABLE example ALTER COLUMN data TYPE text USING substring(data::text,2,length(data::text)-2);
I am trying to write a Query to find if a string contains part of the value in Column (Not to confuse with the query to find if a column contains part of a string).
Say for example I have a column in a table with values
ABC,XYZ
If I give search string
ABCDEFG
then I want the row with ABC to be displayed.
If my search string is XYZDSDS then the row with value XYZ should be displayed
The answer would be "use LIKE".
See the documentation: https://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html
You can do WHERE 'string' LIKE CONCAT(column , '%')
Thus the query becomes:
select * from t1 where 'ABCDEFG' LIKE CONCAT(column1,'%');
If you need to match anywhere in the string:
select * from t1 where 'ABCDEFG' LIKE CONCAT('%',column1,'%');
Here you can see it working in a fiddle:
http://sqlfiddle.com/#!9/d1596/4
Select * from table where #param like '%' + col + '%'
First, you appear to be storing lists of things in a column. This is the wrong approach to storing values in the database. You should have a junction table, with one row per entity and value -- that is, a separate row for ABC and XYZ in your example. SQL has a great data structure for storing lists. It is called a "table", not a "string".
If you are stuck with such a format and using MySQL, there is a function that can help:
where find_in_set('ABC', col)
MySQL treats a comma delimited string as a "set" and offers this function. However, this function cannot use indexes, so it is not particularly efficient. Did I mention that you should use a junction table instead?