How to Order By on a key inside JSONB column in PostgreSQL - sql

I want to get all the details for a particular userID in DESC order of email_subject which is a key present inside "metadata" column of type JSONB.
Metadata is a JSONB column which has below structure:
{
"emails" : [ {"recipients":["userId1#testsite.com"], "email_subject":"Test Subject1"},
{"recipients":["userId2#testsite.com"], "email_subject":"Test Subject2"}]
}
Query:
SELECT * FROM details
WHERE classification = 'EMAIL'
AND EXISTS (SELECT TRUE FROM jsonb_array_elements(metadata->'emails') x
WHERE x->>'recipients' LIKE ('["%userId1#testsite.com%"]') ORDER BY x->>'email_subject' DESC)
ORDER BY metadata->'emails'->>'email_subject' DESC
The above query is able to give me required results for userId "userId1#testsite.com" but I want the final result to be sorted as per this field :
metadata->'emails'->>'email_subject'
I would appreciate all the inputs/suggestions.

Related

Select row with all related child rows as array in one query

I'm using Postgres (latest) with node (latest) PG (latest). Some endpoint is receiving json which looks like:
{
"id": 12345,
"total": 123.45,
"items": [
{
"name": "blue shirt",
"url": "someurl"
},
{
"name": "red shirt",
"url": "someurl"
}
]
}
So I'm storing this in two tables:
CREATE TABLE orders (
id INT NOT NULL,
total NUMERIC(10, 2) DEFAULT 0 NOT NULL,
PRIMARY KEY (id)
);
CREATE INDEX index_orders_id ON orders(id);
CREATE TABLE items (
id BIGSERIAL NOT NULL,
order_id INT NOT NULL,
name VARCHAR(128) NOT NULL,
url VARCHAR(128) DEFAULT '' NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE
);
CREATE INDEX index_items_id ON items(id);
The items table has a FK of order_id to relate the id of the order to its respective items.
Now, the issue is I almost always need to fetch the order along with the items.
How do I get an output similar to my input json in one query?
I know it can be done in two queries, but this pattern will be all over the place and needs to be efficient. My last resort would be to store the items as JSONB column directly in the orders table, but then if I need to query on the items or do joins with them it won't be as easy.
One of many ways:
SELECT jsonb_pretty(
to_jsonb(o.*) -- taking whole row
|| (SELECT jsonb_build_object('items', jsonb_agg(i))
FROM (
SELECT name, url -- picking columns
FROM items i
WHERE i.order_id = o.id
) i
)
)
FROM orders o
WHERE o.id = 12345;
This returns formatted text similar to the displayed input. (But keys are sorted, so 'total' comes after 'items'.)
If an order has no items, you get "items": null.
For a jsonb value, strip the jsonb_pretty() wrapper.
I chose jsonb for its additional functionality - like the jsonb || jsonb → jsonb operator and the jsonb_pretty() function.
Related:
Return multiple columns of the same row as JSON array of objects
If you want a json value instead, you can cast the jsonb directly (without format) or the formatted text (with format). Or build a json value with rudimentary formatting directly (faster):
SELECT row_to_json(sub, true)
FROM (
SELECT o.*
, (SELECT json_agg(i)
FROM (
SELECT name, url -- pick columns to report
FROM items i
WHERE i.order_id = o.id
) i
) AS items
FROM orders o
WHERE o.id = 12345
) sub;
db<>fiddle here
It all depends on what you need exactly.
Aside:
Consider type text (or varchar) instead of the seemingly arbitrary varchar(128). See:
Should I add an arbitrary length limit to VARCHAR columns?

Group on Map value in hive

I have a table in hive
CREATE TABLE IF NOT EXISTS user
(
name STRING,
creation_date DATE,
cards map<STRING,STRING>
) STORED AS PARQUET ;
Let's suppose that I want to query the number of Gobelin cards per user and group by them
My query looks like this :
select card["Gobelin"], COUNT(*) from user GROUP BY card["Gobelin"] ;
I get an error on group by saying
FAILED: SemanticException [Error 10033]: Line 54:30 [] not valid on non-collection types '"Gobelin"': string
As far as I understand, you want to count map elements with key "Gobelin". You can explode the map, then filter out other keys and count the remaining values, e.g.
select count(*) as cnt
from(select explode(cards) as (key, val) from table)
where key = 'Gobelin'
You can use lateral view as well, see Hive's manual for details.

Getting values from array of objects jsonb postgresql

I am storing some ids and names in a jsonb array of object like this
[{"id":"1","name":"abc"},{"id":"2","name":"cde"}]
My table looks like this
id userinfo
1 [{"id":"1","name":"abc"},{"id":"2","name":"cde"}]
2 [{"id":"3","name":"fgh"},{"id":"4","name":"ijk"}]
I am trying to select all the records with id 1 but I just want to get ids in userinfo object I don't want names
I tried this
select distinct userinfo->'name' from table where id = 1
but this is giving me null value
This will work with this query
select distinct userinfo->0->'name' from table where id = 1
but I don't know the index so how can I use this query to get my desired result
Thanks
You need to normalize the data by unnesting the array, then you can access each element.
select ui.info ->> 'id' as id,
ui.info ->> 'name' as name
from the_table t
cross join lateral jsonb_array_elements(t.userinfo) as ui(info)
where t.id = 1;
Online example: http://rextester.com/FCNM11312

Sqlite fetch from table

I have a table named Text_Field which consists of a column named ID,
I have another table named Content which consists of a table named value,
I want to fetch those values of ID from the Text_Field table which are present in the value column of the Content and satisfying a said condition.
I know I can construct a query like this
SELECT ID
FROM Text_Field
WHERE ID IN (
SELECT value
FROM CONTENT
WHERE USER='CURRENT_USER')
My only problem is that for some scenarios the value table might contain the ID inside a string
So the inner query might return something like
56789
12334
12348
Rtf(833405)
Now if my ID is 833405 it is present in the value column but the IN query would return false,
I tried
group_concat(value)
So that the inner query returns a single row which is a string,
56789,12334,12348,Rtf(833405)
I want to know that after group_concat can I use something as LIKE to satisfy my need
Or is there some other way I can do this?
Use exists instead, with like:
SELECT t.ID
FROM Text_Field t
WHERE EXISTS (SELECT 1
FROM CONTENT c
WHERE c.USER = 'CURRENT_USER' AND
(c.value = t.id OR
c.value LIKE '%(' || t.id || ')%'
)
);
Note:

Find most common key, value pairs with Hstore in postgres

I am collecting a list of items and version numbers in an hstore column in postgres. I'm interested in seeing 100 most common key value pairs. For example if this was my data set:
"foo"=> "22",
"foo"=> "33",
"bar"=> "55",
"baz"=> "77",
"foo"=> "22"
I would want to know that "foo"=>"22" is the most common key/value pair in my database. Let's say for ease of talking about the problem that the table name is widgets and the hstore column name is items.
select ??? from widgets;
Is it possible to get a list of the top key value pairs using only SQL?
Oh, this is pretty easy. Here:
SELECT key, count(*) FROM
(SELECT (each(h)).key FROM reports)
AS stat
GROUP BY key
ORDER BY count DESC, key
LIMIT 100;
To get key/value pairs as a set, the relevant function is each():
select * from each('a=>1,b=>2')
http://www.postgresql.org/docs/current/static/hstore.html#HSTORE-FUNC-TABLE
A simple count with a limit can do the trick:
SELECT (item).key, (item).value, count(*) as count
FROM (SELECT each(items) as item FROM widgets) as t
GROUP BY (item).key
ORDER BY 2 DESC, (item).value
LIMIT 100
If you're only interested in the keys, you can use the simpler skeys() instead:
SELECT k, count(*) as count
FROM (SELECT skeys(items) as k FROM widgets) as t
GROUP BY k
ORDER BY 2 DESC, k
LIMIT 100
As you are after the complete key/value pair, the following should do it:
select items, count(*) as cnt
from widgets
group by items
order by 2 desc
limit 100