How to query JSON column for unique object values in PostgreSQL - sql

I'm looking to query a table for a distinct list of values in a given JSON column.
In the code snippet below, the Survey_Results table has 3 columns:
Name, Email, and Payload. Payload is the JSON object to I want to query.
Table Name: Survey_Results
Name Email Payload
Ying SmartStuff#gmail.com [
{"fieldName":"Product Name", "Value":"Calculator"},
{"fieldName":"Product Price", "Value":"$54.99"}
]
Kendrick MrTexas#gmail.com [
{"fieldName":"Food Name", "Value":"Texas Toast"},
{"fieldName":"Food Taste", "Value":"Delicious"}
]
Andy WhereTheBass#gmail.com [
{"fieldName":"Band Name", "Value":"MetalHeads"}
{"fieldName":"Valid Member", "Value":"TRUE"}
]
I am looking for a unique list of all fieldNames mentioned.
The ideal answer would be query giving me a list containing "Product Name", "Product Price", "Food Name", "Food Taste", "Band Name", and "Valid Member".
Is something like this possible in Postgres?

Use jsonb_array_elements() in a lateral join:
select distinct value->>'fieldName' as field_name
from survey_results
cross join json_array_elements(payload)
field_name
---------------
Product Name
Valid Member
Food Taste
Product Price
Food Name
Band Name
(6 rows)
How to find distinct Food Name values?
select distinct value->>'Value' as food_name
from survey_results
cross join json_array_elements(payload)
where value->>'fieldName' = 'Food Name'
food_name
-------------
Texas Toast
(1 row)
Db<>fiddle.
Important. Note that the json structure is illogical and thus unnecessarily large and complex. Instead of
[
{"fieldName":"Product Name", "Value":"Calculator"},
{"fieldName":"Product Price", "Value":"$54.99"}
]
use
{"Product Name": "Calculator", "Product Price": "$54.99"}
Open this db<>fiddle to see that proper json structure implies simpler and faster queries.

Related

Joining two tables of data via ID but the ID for one table is 5 more that the other

I'm trying to join two tables of data using ID's. ID's from one table correspond to an ID+5 on the other table.
I've tried using this code:-
SELECT
"Stage",
"Type",
"Contract Type (Business Or Personal)",
"Requested Vehicle",
"Short Vehicle",
"Mileage Per Year",
"Initial Rental",
"Initial Rental £",
"Contract Length",
Opportunities."Advertised Rental",
Contacts.Id,
Opportunities.Id,
"First Name",
"Full Name",
"Email",
"Mobile",
"Phone",
"Contact Owner Name",
Contacts."Lead Source",
Contacts."Created Time"
FROM "Opportunities"
RIGHT JOIN "Contacts" ON Opportunities.Id = Contacts.Id
ORDER BY Contacts."Created Time"
This results in only the contacts data showing up in a table with the opportunities data blank. This is because the ID's are different by 5. So I tried:-
RIGHT JOIN "Contacts" ON Opportunities.Id-5 = Contacts.Id
but that didn't work and I couldn't find anything on the internet that was relevant. Any help would be appreciated as I'm an inexperienced coder.
Thanks,
James
Have you just tried using an alias for a column?
SELECT
"Stage",
"Type",
"Contract Type (Business Or Personal)",
"Requested Vehicle",
"Short Vehicle",
"Mileage Per Year",
"Initial Rental",
"Initial Rental £",
"Contract Length",
Opportunities."Advertised Rental",
Contacts.Id,
Opportunities.Id,
"First Name",
"Full Name",
"Email",
"Mobile",
"Phone",
"Contact Owner Name",
Contacts."Lead Source",
Contacts."Created Time"
FROM (SELECT Opportunities.*, "Id" - 5 AS "ContactId" FROM "Opportunities") AS Opps
RIGHT JOIN "Contacts" ON Opps.ContactId = Contacts.Id
ORDER BY Contacts."Created Time"
Side note, it's probably good to scrub your data to have a cleaner reference than an offset ID if you have permissions to do that.

How to expand a list of dict in presto db

I have a column in prestodb that is a list of dictionaries:
[{"id": 45238, "kind": "product", "name": "Ball", "category": "toy"}, {"id": 117852, "kind": "service", "name": "courier", "category": "transport"}]
is a there a way to expand this column to get something like this:
id kind name category
4528 product Ball toy
117852 service courier transport
Also sometimes the key's can be different from the example above also can have more key's than the 4 above
I am trying:
with cte as ( select cast(divs as json) as json_field from table)
select m['id'] id,
m['kind'] kind,
m['name'] name,
m['category'] category
from cte
cross join unnest(cast(json_field as array(map(varchar, json)))) as t(m)
Error:
INVALID_CAST_ARGUMENT: Cannot cast to array(map(varchar, json)). Expected a json array, but got [{"id": 36112, "kind"....
Assuming your data contains json - you can cast it to array of maps from varchar to json (array(map(varchar, json))) and then use unnest to flatten the array:
WITH dataset (json_str) AS (
VALUES (json '[{"id": 45238, "kind": "product", "name": "Ball", "category": "toy"}, {"id": 117852, "kind": "service", "name": "courier", "category": "transport"}]')
)
select m['id'] id,
m['kind'] kind,
m['name'] name,
m['category'] category
from dataset
cross join unnest(cast(json_str as array(map(varchar, json)))) as t(m)
id
kind
name
category
45238
product
Ball
toy
117852
service
courier
transport
UPD
If original column type is varchar - use json_parse to convert it to json.

Snowflake SQL: How to loop through array with JSON objects, to find item that meets condition

Breaking my head on this. In Snowflake my field city_info looks like (for 3 sample records)
[{"name": "age", "content": 35}, {"name": "city", "content": "Chicago"}]
[{"name": "age", "content": 20}, {"name": "city", "content": "Boston"}]
[{"name": "city", "content": "New York"}, {"name": "age", "content": 42}]
I try to extract a column city from this
Chicago
Boston
New York
I tried to flatten this
select *
from lateral flatten(input =>
select city_info::VARIANT as event
from data
)
And from there I can derive the value, but this only allows me to do this for 1 row (so I have to add limit 1 which doesn't makes sense, as I need this for all my rows).
If I try to do it for the 3 rows it tells me subquery returns more than one row.
Any help is appreciated! Chris
You could write it as:
SELECT value:content::string AS city_name
FROM tab,
LATERAL FLATTEN(input => tab.city_info)
WHERE value:name::string = 'city'

SQL query to return nested array of objects in JSON for SQLite

I have 2 simple tables in a SQLite db and a nodejs, express api endpoint that should get results by student and have the subjects as a nested array of objects.
Tables:
Student(id, name) and Subject(id, name, studentId)
This is what I need to result to look like:
{
"id": 1,
"name": "Student name",
"subjects":
[{
"id": 1,
"name": "Subject 1"
},
{
"id": 2,
"name": "Subject 2"
}]
}
How can I write a query to get this result?
If your version of sqlite was built with support for the JSON1 extension, it's easy to generate the JSON from the query itself:
SELECT json_object('id', id, 'name', name
, 'subjects'
, (SELECT json_group_array(json_object('id', subj.id, 'name', subj.name))
FROM subject AS subj
WHERE subj.studentid = stu.id)) AS record
FROM student AS stu
WHERE id = 1;
record
---------------------------------------------------------------------------------------------------
{"id":1,"name":"Student Name","subjects":[{"id":1,"name":"Subject 1"},{"id":2,"name":"Subject 2"}]}
It seems that all you need is a LEFT JOIN statement:
SELECT subject.id, subject.name, student.id, student.name
FROM subject
LEFT JOIN student ON subject.studentId = student.id
ORDER BY student.id;
Then just parse the rows of the response into the object structure you require.

PostgreSQL Select rows based on combination of array values

I would like to select all rows from my database where one row contains at least two terms from a set of words/array.
As an example:
I have the following array:
'{"test", "god", "safe", "name", "hello", "pray", "stay", "word", "peopl", "rain", "lord", "make", "life", "hope", "whatever", "makes", "strong", "stop", "give", "television"}'
and I got a tweet dataset stored in the database. So i would like to know which tweets (column name: tweet.content) contain at least two of the words.
My current code looks like this (but of course it only selects one word...):
CREATE OR REPLACE VIEW tweet_selection AS
SELECT tweet.id, tweet.content, tweet.username, tweet.geometry,
FROM tweet
WHERE tweet.topic_indicator > 0.15::double precision
AND string_to_array(lower(tweet.content)) = ANY(SELECT '{"test", "god", "safe", "name", "hello", "pray", "stay", "word", "peopl", "rain", "lord", "make", "life", "hope", "whatever", "makes", "strong", "stop", "give", "television"}'::text[])
so the last line needs to be adjustested somehow, but i have no idea how - maybe with a inner join?!
I have the words also stored with a unique id in a different table.
A friend of mine recommended getting a count for each row, but i have no writing access for adding an additional column in the original tables.
Background:
I am storing my tweets in a postgres database and I applied a LDA (Latent dirichlet allocation) on the dataset. Now i got the generated topics and the words associated with each topic (20 topics and 25 words).
select DISTINCT ON (tweet.id) tweet.id, tweet.content, tweet.username, tweet.geometry
from tweet
where
tweet.topic_indicator > 0.15::double precision
and (
select count(distinct word)
from
unnest(
array['test', 'god', 'safe', 'name', 'hello', 'pray', 'stay', 'word', 'peopl', 'rain', 'lord', 'make', 'life', 'hope', 'whatever', 'makes', 'strong', 'stop', 'give', 'television']::text[]
) s(word)
inner join
regexp_split_to_table(lower(tweet.content), ' ') v (word) using (word)
) >= 2