jsonb aggegation in postgres select queries - postgresql

Given the following table:
# create table thing (id serial, tags jsonb);
# \d thing
Table "public.thing"
Column | Type | Modifiers
--------+---------+----------------------------------------------------
id | integer | not null default nextval('thing_id_seq'::regclass)
tags | jsonb |
...and the following data:
insert into thing (tags) values ('{"tag1": ["val1", "val2"], "tag2": ["t2val1"]}');
insert into thing (tags) values ('{"tag1": ["val3", "val1"], "tag2": ["t2val1"]}');
insert into thing (tags) values ('{"tag1": ["val2", "val1"], "tag2": ["t2val2"]}');
How can I aggregate the results of a query that equates to "show me the number of matching rows and the set of tag1 value that have a tag2 value of t2val1?
The closes I can get is:
# select count(*), json_agg(tags) from thing where tags->'tag2'?'t2val1';
count | json_agg
-------+--------------------------------------------------------------------------------------------------
2 | [{"tag1": ["val1", "val2"], "tag2": ["t2val1"]}, {"tag1": ["val3", "val1"], "tag2": ["t2val1"]}]
(1 row)
...but I really want:
count | tag1
-------+-------------------------
2 | ["val1", "val2", "val3"]
(1 row)

Related

Why is my equal not working on 2 identical String

I'm using postgresql 13 and I'm trying to fetch data from a table based on one of its column.
Said table is defined as follow :
create table my_table (
my_table_id int8 not null,
value varchar(255) not null,
another_table_id int8 not null,
primary key (my_table_id) );
create index my_table__lower_value__idx
ON my_table USING btree (lower((value)::text));
Now, when I'm running both query :
first to select a row with a where clause based on a value defined in another table (column my_table_id)
second to select the same row and the same table based on a value defined in this table (column value).
Second query is not returning any row.
See below :
db > select * from my_table where my_table_id = 1001;
my_table_id | value | another_table_id
------------+--------+-----------------
1 | value1 | 1001
(1 row)
db > select * from my_table where lower(value) = lower('value1');
my_table_id | value | another_table_id
------------+--------+-----------------
(0 rows)
Mind you, if I ran this query with some other values, it works :
db > select * from my_table where my_table_id = 1002;
my_table_id | value | another_table_id
------------+--------+-----------------
2 | value2 | 1002
(1 row)
db > select * from my_table where lower(value) = lower('value2');
my_table_id | value | another_table_id
------------+---------+-----------------
2 | value2 | 1002
(1 row)
Why this difference ?
What I've tried so far :
using select * from my_table where value in (select value from my_table where another_table_id = 1001); does not work
using lower on each part of equal statement: still not working on first case.
using LIKE keyword : it works fine in both cases

extract all values of postgresql jsonb object

i have a postgresql table t1 , id integer , data jsonb
id | data
--------------------
1 | {"1":{"11":11},"2":{"12":12}}
and i need a function to extract all key/value in separate rows
like this
key | values
----------------------
1 | {"11":11}
2 | {"12":12}
in "hstore" dataType , there was "hvals" function , do this
but in jsonb i dont find similar function
You are looking for jsonb_each
with t1 (id, data) as (
values (1, '{"1":{"11":11},"2":{"12":12}}'::jsonb)
)
select t.*
from t1, jsonb_each(data) as t(k,v)
returns:
k | v
--+-----------
1 | {"11": 11}
2 | {"12": 12}

Insert based on select of hstore column

In try to insert value from a hstore (postgreql) to a more generic table
In my car table, I have theses fields
id
fields (hstore)
My store table, I have theses fields
id
key
value
car_id
date
How to loop to my fields property in insert key, value to my store table.
Is there a way to do it with a select command?
Example data:
insert into car values
(1, 'brand=>ford, color=>yellow'),
(2, 'brand=>volvo, mileage=>50000, year=>2015');
Use the function each(hstore) to get pairs (key, value) of hstore column:
select id, key, value
from car, each(fields);
id | key | value
----+---------+--------
1 | brand | ford
1 | color | yellow
2 | year | 2015
2 | brand | volvo
2 | mileage | 50000
(5 rows)
The insert command may look like this:
insert into store (car_id, key, value)
select id, key, value
from car, each(fields);

PostgreSQL Group By not working as expected - wants too many inclusions

I have a simple postgresql table that I'm tying to query. Imaging a table like this...
| ID | Account_ID | Iteration |
|----|------------|-----------|
| 1 | 100 | 1 |
| 2 | 101 | 1 |
| 3 | 100 | 2 |
I need to get the ID column for each Account_ID where Iteration is at its maximum value. So, you'd think something like this would work
SELECT "ID", "Account_ID", MAX("Iteration")
FROM "Table_Name"
GROUP BY "Account_ID"
And I expect to get:
| ID | Account_ID | MAX(Iteration) |
|----|------------|----------------|
| 2 | 101 | 1 |
| 3 | 100 | 2 |
But when I do this, Postgres complains:
ERROR: column "ID" must appear in the GROUP BY clause or be used in an aggregate function
Which, when I do that it just destroys the grouping altogether and gives me the whole table!
Is the best way to approach this using the following?
SELECT DISTINCT ON ("Account_ID") "ID", "Account_ID", "Iteration"
FROM "Marketing_Sparks"
ORDER BY "Account_ID" ASC, "Iteration" DESC;
The GROUP BY statement aggregates rows with the same values in the columns included in the group by into a single row. Because this row isn't the same as the original row, you can't have a column that is not in the group by or in an aggregate function. To get what you want, you will probably have to select without the ID column, then join the result to the original table. I don't know PostgreSQL syntax, but I assume it would be something like the following.
SELECT Table_Name.ID, aggregate.Account_ID, aggregate.MIteration
(SELECT Account_ID, MAX(Iteration) AS MIteration
FROM Table_Name
GROUP BY Account_ID) aggregate
LEFT JOIN Table_Name ON aggregate.Account_ID = Table_Name.Account_ID AND
aggregate.MIteration = Tabel_Name.Iteration

Complex TSQL MultiRow Insert with OutPut

I have a temp table as follows
DECLARE #InsertedRows TABLE (RevId INT, FooId INT)
I also have two other tables
Foo(FooId INT, MyData NVarchar(20))
Revisions(RevId INT, CreatedTimeStamp DATETIME)
For each row in Foo, I need to a) insert a row into Revisions and b) insert a row into #InsertedRows with the corresponding Id values from Foo and Revisions.
I've tried writing something using the Insert Output Select as follows:
INSERT INTO Revisions (CURRENT_TIMESTAMP)
OUTPUT Inserted.RevId, Foo.FooId INTO #InsertedRows
SELECT FooId From Foo
However, Foo.Id is not allowed in the Output column list. Also, the Id returned in the SELECT isn't inserted into the table, so that's another issue.
How can I resolve this?
You cannot reference the FROM table in an OUTPUT clause with an INSERT statement. You can only do this with a DELETE, UPDATE, or MERGE statement.
From the MSDN page on the OUTPUT clause (https://msdn.microsoft.com/en-us/library/ms177564.aspx)
from_table_name Is a column prefix that specifies a table included in
the FROM clause of a DELETE, UPDATE, or MERGE statement that is used
to specify the rows to update or delete.
You can use a MERGE statement to accomplish what you are asking.
In the below example, I changed the tables to be all variable tables so that this could be run as an independent query and I changed the ID columns to IDENTITY columns which increment differently to illustrate the relationship.
The ON clause (1=0) will always evaluate to NOT MATCHED. This means that all records in the USING statement will be used to insert into the target table. Additionally the FROM table in the USING statement will be available to use in the OUTPUT statement.
DECLARE #Foo TABLE (FooId INT IDENTITY(1,1), MyData NVarchar(20))
DECLARE #Revisions TABLE (RevId INT IDENTITY(100,10), CreatedTimeStamp DATETIME)
DECLARE #InsertedRows TABLE (RevId INT, FooId INT)
INSERT INTO #Foo VALUES ('FooData1'), ('FooData2'), ('FooData3')
MERGE #Revisions AS [Revisions]
USING (SELECT FooId FROM #Foo) AS [Foo]
ON (1=0)
WHEN NOT MATCHED THEN
INSERT (CreatedTimeStamp) VALUES (CURRENT_TIMESTAMP)
OUTPUT INSERTED.RevId, Foo.FooId INTO #InsertedRows;
SELECT * FROM #Foo
SELECT * FROM #Revisions
SELECT * FROM #InsertedRows
Table results from above query
#Foo table
+-------+----------+
| FooId | MyData |
+-------+----------+
| 1 | FooData1 |
| 2 | FooData2 |
| 3 | FooData3 |
+-------+----------+
#Revisions table
+-------+-------------------------+
| RevId | CreatedTimeStamp |
+-------+-------------------------+
| 100 | 2016-03-31 14:48:39.733 |
| 110 | 2016-03-31 14:48:39.733 |
| 120 | 2016-03-31 14:48:39.733 |
+-------+-------------------------+
#InsertedRows table
+-------+-------+
| RevId | FooId |
+-------+-------+
| 100 | 1 |
| 110 | 2 |
| 120 | 3 |
+-------+-------+