Search in all JSON fields with PostgreSQL - sql

How could I query a JSON field to search in all fields of the JSON?
Table
id | JSON
--------------------------
1 | {"name":"toto","age":25,"surname":"toto2"}
I'd like to query on every fields of the JSON, because I'm programming a global search function, and i need for example to return every row containing "toto" in its JSON.
I managed to do it in SqlServer 2016 with OPEN_JSON function.
Here is my code
SELECT DISTINCT *
FROM ObjectsTable
CROSS APPLY OPENJSON([JSON]) as tbValues
WHERE tbValues.value like '%toto%'
I need to do the same thing in Postgres.

Essentially the same in Postgres, but cross apply is cross join lateral in the SQL standard and in Postgres
select o.id, t.*
from objectstable o
cross join lateral json_each_text(o.json) as t(k,val)
where t.val like '%toto%'

Related

Cross joining to an unnested mapping field in HQL (works in athena, not in Hive)

So I have two (mapping) fields I need to unpack and break out into rows. In athena, I can use the following approach (to unpack iether of them:
SELECT
unique_id,
key,
value
FROM
(
select
unique_id,
purchase_history
from table
)
CROSS JOIN unnest(purchase_history) t(key,value)
This works perfectly in athena, I get 1 row for each purchase along with their unique identifier. However, when I try to test it in Hive it doesn't work. Is there anything specific in here that doesn't fly in HQL? I think cross joins are allowed, but perhaps the way I am calling the field isn't working? Or is it the "unnest"? Please let me know if you need further explanation.
You can do the same in Hive using lateral view explode, if purchase_history is of type map, this will work:
SELECT
s.unique_id,
t.key,
t.value
FROM
(
select
unique_id,
purchase_history
from table
) s --alias for sub-queries is a must in Hive
lateral view explode(s.purchase_history) t as key,value

Unpivot SQL Table with multiple columns

I would like to unpivot a SQL table around multiple columns.
I have tried a normal UNPIVOT statement but that only ppivots around one value.
See this link for example: https://codingsight.com/understanding-pivot-unpivot-and-reverse-pivot-statements.
I have tried to illustrate my data as well as my desired outcome in the picture below.
The top table is a sample of the data in the SQL table. I have used 3 materials but in reality there are 20.
The bottom table is my desired outcome.
The data is on a SQL 2008-r2 server.
Any pointers on how to go about this task?
Consider using cross apply, like so:
select t.date, t.product, x.*
from mytable t
cross apply (values
(container1material, container1amount),
(container2material, container2amount),
(container3material, container3amount)
) x(material, amount)
Use apply for unpivoting:
select t.date, t.product, v.*
from t cross apply
(values (container1amount, container1material),
(container2amount, container2material),
(container3amount, container3material)
) v(containeramount, containermaterial);
unpivot is bespoke syntax (not-standard) and it only does one thing. By contrast, lateral joins are very powerful and unpivoting is only one thing that you can do with them. Apply is worth learning about.

SQL Pivot Table Multiple Columns

I have a table that looks like this:
Title01 Title02 Title03 Title04 Title05
Number Title Division Department IFC
And I am wanting to turn the columns into rows so it loos like this:
Field
Number
Title
Division
Department
IFC
Is it possible to do this using the PIVOT function in SQL?
I like to use CROSS APPLY for this:
select v.field
from t cross apply
(values (title01), (title02), (title03), (title04), (title05)
) v(field);
CROSS APPLY implements the lateral join. You can think of it as an extension of correlated subqueries -- but the subquery can return multiple columns and multiple rows. Unpivoting data happens to be a simple introduction to the concept.

GROUP BY in Postgres - no equality for JSON data type?

I have the following data in a matches table:
5;{"Id":1,"Teams":[{"Name":"TeamA","Players":[{"Name":"AAA"},{"Name":"BBB"}]},{"Name":"TeamB","Players":[{"Name":"CCC"},{"Name":"DDD"}]}],"TeamRank":[1,2]}
6;{"Id":2,"Teams":[{"Name":"TeamA","Players":[{"Name":"CCC"},{"Name":"BBB"}]},{"Name":"TeamB","Players":[{"Name":"AAA"},{"Name":"DDD"}]}],"TeamRank":[1,2]}
I want to select each last distinct Team in the table by their name. i.e. I want a query that will return:
6;{"Name":"TeamA","Players":[{"Name":"CCC"},{"Name":"BBB"}
6;{"Name":"TeamB","Players":[{"Name":"AAA"},{"Name":"DDD"}
So each team from last time that team appears in the table.
I have been using the following (from here):
WITH t AS (SELECT id, json_array_elements(match->'Teams') AS team FROM matches)
SELECT MAX(id) AS max_id, team FROM t GROUP BY team->'Name';
But this returns:
ERROR: could not identify an equality operator for type json
SQL state: 42883
Character: 1680
I understand that Postgres doesn't have equality for JSON. I only need equality for the team's name (a string), the players on that team don't need to be compared.
Can anyone suggest an alternative way to do this?
For reference:
SELECT id, json_array_elements(match->'Teams') AS team FROM matches
returns:
5;"{"Name":"TeamA","Players":[{"Name":"AAA"},{"Name":"BBB"}]}"
5;"{"Name":"TeamB","Players":[{"Name":"CCC"},{"Name":"DDD"}]}"
6;"{"Name":"TeamA","Players":[{"Name":"CCC"},{"Name":"BBB"}]}"
6;"{"Name":"TeamB","Players":[{"Name":"AAA"},{"Name":"DDD"}]}"
EDIT: I cast to text and following this question, I used DISTINCT ON instead of GROUP BY. Here's my full query:
WITH t AS (SELECT id, json_array_elements(match->'Teams') AS team
FROM matches ORDER BY id DESC)
SELECT DISTINCT ON (team->>'Name') id, team FROM t;
Returns what I wanted above. Does anyone have a better solution?
Shorter, faster and more elegant with a LATERAL join:
SELECT DISTINCT ON (t.team->>'Name') t.team
FROM matches m, json_array_elements(m.match->'Teams') t(team);
ORDER BY t.team->>'Name', m.id DESC; -- to get the "last"
If you just want distinct teams, the ORDER BY can go. Related:
Query for element of array in JSON column
Query for array elements inside JSON type
JSON and equality
There is no equality operator for the json data type in Postgres, but there is one for jsonb (Postgres 9.4+):
How to query a json column for empty objects?

Oracle to PostgreSQL query conversion with string_to_array()

I have below query in Oracle:
SELECT to_number(a.v_VALUE), b.v_VALUE
FROM TABLE(inv_fn_splitondelimiter('12;5;25;10',';')) a
JOIN TABLE(inv_fn_splitondelimiter('10;20;;', ';')) b
ON a.v_idx = b.v_idx
which give me result like:
I want to convert the query to Postgres. I have tried a query like:
SELECT UNNEST(String_To_Array('10;20;',';'))
I have also tried:
SELECT a,b
FROM (select UNNEST(String_To_Array('12;5;25;10;2',';'))) a
LEFT JOIN (select UNNEST(String_To_Array('12;5;25;10',';'))) b
ON a = b
But didn't get a correct result.
I don't know how to write query that's fully equivalent to the Oracle version. Anyone?
Starting with Postgres 9.4 you can use unnest() with multiple arrays to unnest them in parallel:
SELECT *
FROM unnest('{12,5,25,10,2}'::int[]
, '{10,20}' ::int[]) AS t(col1, col2);
That's all. NULL values are filled in automatically for missing elements to the right.
If parameters are provided as strings, convert with string_to_array() first. Like:
SELECT *
FROM unnest(string_to_array('12;5;25;10', ';')
, string_to_array('10;20' , ';')) AS t(col1, col2);
More details and an alternative solution for older versions:
Unnest multiple arrays in parallel
Split given string and prepare case statement
In the expression select a the a is not a column, but the name of the table alias. Consequently that expressions selects a complete row-tuple (albeit with just a single column), not a single column.
You need to define proper column aliases for the derived tables. It is also recommended to use set returning functions only in the from clause, not in the select list.
If you are not on 9.4 you need to generate the "index" using a window function. If you are on 9.4 then Erwin's answer is much better.
SELECT a.v_value, b.v_value
FROM (
select row_number() over () as idx, -- generate an index for each element
i as v_value
from UNNEST(String_To_Array('12;5;25;10;2',';')) i
) as a
JOIN (
select row_number() over() as idx,
i as v_value
from UNNEST(String_To_Array('10;20;;',';')) i
) as b
ON a.idx = b.idx;
An alternative way in 9.4 would be to use the with ordinality option to generate the row index in case you do need the index value:
select a.v_value, b.v_value
from regexp_split_to_table('12;5;25;10;2',';') with ordinality as a(v_value, idx)
left join regexp_split_to_table('10;20;;',';') with ordinality as b(v_value, idx)
on a.idx = b.idx