Postgres query json path within array - json

What would be the following expression to get the value 20 here?
SELECT ('{"a": [{"age":10}]}'::jsonb)->'a.*.age';
As the above currently gives me null.

You have to separate the paths, for example:
SELECT ('{"a": [{"age":10}]}'::jsonb)->'a' -> 0 -> 'age';
# 10

Related

How to return a path with '*' in Postgres

I can use a particular index to get data from a known location:
SELECT ('{"a": [{"age":10},{"age":20}]}'::jsonb)->'a' -> 0 -> 'age';
# 10
However, how would I get all values at that location? I want to get [10, 20] with something like:
SELECT ('{"a": [{"age":10},{"age":20}]}'::jsonb)->'a' -> '*' -> 'age';
But I guess the * operator isn't supported here? How would I accomplish this?
The native operators won't work here but JSONPath is supported in Postgres and you can use one of those functions that supports it, for example:
WITH tbl(v) AS (
SELECT '{"a": [{"age":10},{"age":20}]}'::jsonb
)
SELECT jsonb_path_query(v, '$.a[*].age') FROM tbl;
------------------
10
20
(2 rows)

How to properly format overlapping mySQL IN and NOT IN conditions

I have the following mySQL table:
data
1
2
3
4
5
6
7
8
9
I would like to supply my select statement with two seperate lists
Exculde List:
1,4,5,7
Include List:
1,2,3,4,5,6,7
I tried the following statement:
Select * FROM table WHERE data NOT IN ('1,4,5,7') AND data IN ('1,2,3,4,5,6,7)
Expecting the following output:
data
2
3
6
But I received no results. I realize I passed an impossible condition but I don't know how to format my query to return the expected results.
Can anyone tell me what I'm doing wrong here?
IN takes a list of values, not a string that holds a delimited list of values.
Examples:
x IN (1, 2, 3)
x IN ('a', 'b', 'c')
Use IN (1,2,3) and not IN ('1,2,3') as the former compares to individual values 1, 2 and 3 while the latter is against the literal string 1,2,3.
Select * FROM ( (Select * FROM table WHERE data NOT IN ('1,4,5,7') ) AS table WHERE data IN ('1,2,3,4,5,6,7)
you try againt

How to search multiple items in JSON array in Postgres 9.3

I have scenario where i need to search multiple values in a JSON array. Below is my schema.
ID DATA
1 {"bookIds" : [1,2,3,5], "storeIds": [2,3]}
2 {"bookIds" : [1,2], "storeIds": [1,3]}
3 {"bookIds" : [11,12,10,9], "storeIds": [4,3]}
I want all the rows with value 1,2. Below is query i am using (This is query is written by one of fellow stackoverflow user Mr. klin credit to him).
select t.*
from JSONTest t, json_array_elements(data->'bookIds') books
where books::text::int in (1, 2);
However output I am duplicate rows in output, below is my output.
id data
1 {"bookIds" : [1,2,3,5], "storeIds": [2,3]}
1 {"bookIds" : [1,2,3,5], "storeIds": [2,3]}
2 {"bookIds" : [1,2], "storeIds": [1,3]}
2 {"bookIds" : [1,2], "storeIds": [1,3]}
I want only two rows in output that is id 1,2. How can i do that? I don't want use Distinct due to other constraints,
SQL Fiddle : http://sqlfiddle.com/#!15/6457a/2
Unfortunately there is no direct conversion function from a JSON array to a "real" Postgres array. (data ->'bookIds')::text returns something that is nearly a Postgres array literal: e.g. [1,2,3,5]. If you replace the [] with {} the value can be cast to an integer array. Once we have a proper integer array we can use the #> to test if it contains another array:
select *
from jsontest
where translate((data ->'bookIds')::text, '[]', '{}')::int[] #> array[1,2];
translate((data ->'bookIds')::text, '[]', '{}') will convert [1,2,3,5] to {1,2,3,5} which then is converted to an array using ::int[]
SQLFiddle: http://sqlfiddle.com/#!15/6457a/4

String conversion in hive

I have a json string in which there isa field called version.
Version can either not be there or if it is there it will be of form x.y
.
I want to convert this to x.0 I am currently doing
CONCAT(split(get_json_object(json, '$.version'),'[.]')[0],".","0")
but this does not handle cases where version is not there.
I want "bad_version" to be returned if version is not there. Can I somehow use COALESCE and do some tweaks ?
Yes, you can use either COALESCE or CASE - the syntax is identical to database usage.
select coalesce(myField, 'bad_version') ....
or
select case when myField is null then 'bad_version' else myField end as x ....
You can conditionally test the result of get_json_object to see if it's NULL and return bad_version accordingly. When the version is valid, you can use a regular expression to replace the minor version with 0.
SELECT
IF(get_json_object(json, "$.version") IS NULL,
"bad_version",
regexp_replace(get_json_object(json, "$.version") , "\\..*$", ".0")
)
FROM json_table; -- The table I loaded with test data
Some simple example data:
hive> SELECT json FROM json_table;
OK
{"id":"001","version":"3.9"}
{"id":"002","notversion":"3.9"}
Time taken: 0.225 seconds, Fetched: 2 row(s)
And then the results of this query against this data:
hive> SELECT
> IF(get_json_object(json, "$.version") IS NULL,
> "bad_version",
> regexp_replace(get_json_object(json, "$.version") , "\\..+$", ".0")
> )
> FROM json_table;
OK
3.0
bad_version
Time taken: 0.225 seconds, Fetched: 2 row(s)

Naturally ORDER a column containing hierarchical item names

I have a column (of VARCHAR type) with values like these:
1
2
3
1.1
1.1.1
1.2.1
5
4
7
8
9
10
10.2
10.1
I hop[e to select this column and order it naturally, like the following:
1
1.1
1.1.1
1.2.1
2
3
4
5
...
I have tried ordering it with this for example and a lot of other query
SELECT data
FROM sample
ORDER BY LEN(data), data
Does someone have an idea how to do this?
try this
ORDER BY data, LEN(data)
or this
ORDER BY CONVERT(SUBSTRING_INDEX(data, ',', -1), SIGNED), Len(data)
i give demo in mysql as tsql there is not in sqfiddle .
DEMO
You seem to be hoping to order a series of hierarchically named items in a natural order. It looks like these items' names take the form.
token [ .token [. token [ .token ]]]
where subsequent tokens after the first are optional.
I suppose you want each token, if it's numeric, to be handed as a number. That is, for example, you want 1.123 to come after 1.2 because 123 is numerically greater than 2.
You didn't say what you want done with alphabetical tokens, e.g. 401.k and 403.b. I suppose they should come after the numerical ones, but in lexical order.
This query (http://sqlfiddle.com/#!2/81756/2/0) will do the trick out to five hierarchical levels of tokens.
SELECT col
FROM T
ORDER BY
FLOOR(SUBSTRING_INDEX(col,'.',1)),
SUBSTRING_INDEX(col,'.',1),
FLOOR(SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',1)))),
SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',1))),
FLOOR(SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',2)))),
SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',2))),
FLOOR(SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',3)))),
SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',3))),
FLOOR(SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',4)))),
SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',4)))
Why does this work? FLOOR() converts the leftmost part of a string to an integer, so it picks up the leading integer. If it doesn't find any numbers in the string it's trying to convert, it returns zero.
And, SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',NNN))) picks up the part of the col item to the right of the NNNth dot.
The data seems something like Hierarchical. With the current set of data supplied, if data is converted to hierarchical Data the order by can be done using something similar to:-
SELECT data FROM sample ORDER BY CAST ( '/' + replace( data, '.',
'/' ) + '/' as hierarchyid )