Do UPSERT based on specific value of JSON in Postgres 10 - sql

I have a Postgres table messages as follows:
Column | Type | Collation | Nullable |
-----------+--------------------------+-----------+----------
id | integer | | not null |
message | jsonb | | |
date | timestamp with time zone | | not null |
id | message | date
1 | {"name":"alpha", "pos":"x"} | 2020-02-11 12:31:44.658667+00
2 | {"name":"bravo", "pos":"y"} | 2020-02-11 12:32:43.123678+00
3 | {"name":"charlie", "pos":"z"}| 2020-02-11 12:38:37.623535+00
What I would like to do is do an UPSERT based on the value of the name key i.e., if there is an insert with same name value, then the other value pos is updated, otherwise a new entry is created.
I did CREATE UNIQUE INDEX message_name ON messages((message->>'name'));
I found the INSERT ON CONFLICT in Postgres 9.5+ but I can't understand how to use the unique index with this.
I don't know if this is the correct approach to do it in the first place so if there is a better way to do this, I would appreciate the input.

You need to repeat the expression from the index:
insert into messages (message)
values ('{"name":"alpha", "pos":"new pos"}')
on conflict ((message->>'name'))
do update
set message = jsonb_set(messages.message, '{pos}'::text[], excluded.message -> 'pos', true)
;
If you have more keys in the JSON and want to replace (or add) all of them, you can use this:
insert into messages (message)
values ('{"name":"alpha", "pos":"new pos", "some key": 42}')
on conflict ((message->>'name'))
do update
set message = messages.message || (excluded.message - 'name')
;

Related

posgresql: relation does not exist

I have ported a database to a new laptop by using an sql dump from the old computer and then executing this dump on the new computer.
Thea data was copied correctly, and all the SQL statements regarding the tables work perfectly.
For creating new relations i need to know the values of some of the sequences in the DB:
italo=> \ds
List of relations
Schema | Name | Type | Owner
--------+--------------------------+----------+-------
public | Conjugation_conjType_seq | sequence | italo
public | Conjugation_idConj_seq | sequence | italo
public | NounItalian_idNounIt_seq | sequence | italo
public | VerbItalian_idVerbIt_seq | sequence | italo
public | times_idTime_seq | sequence | italo
(5 rows)
When i use currval() in a select statement i get an error:
italo=> SELECT currval('VerbItalian_idVerbIt_seq');
ERROR: relation "verbitalian_idverbit_seq" does not exist
LINE 1: SELECT currval('VerbItalian_idVerbIt_seq');
When i look at a sequence in phppgadmin, i get the same error message about the missing relation followed by a column view of the sequence. The error message reads
SQL error:
ERROR: relation "public.verbitalian_idverbit_seq" does not exist
In statement:
SELECT pg_catalog.has_sequence_privilege('public.VerbItalian_idVerbIt_seq','SELECT,USAGE')
Name | Start value | Last value | Increment by | Max value | Min value | Cache value | Log count | Can cycle? | Will increment last value before returning next value (is_called)?
VerbItalian_idVerbIt_seq | 100 | | 1 | 9223372036854775807 | 1 | 1 | | No | No
Alter Set value Increment value Restart Reset Show all sequences
(the values for "Last value" and "Log count" are empty)
When i click on "Set value", i can enter a value for "Last value", and there is small message "Sequence value set.", but in the view nothing has changed.
The behavior is the same for all 5 sequences.
What do i have to do to fix my sequences so that i can use currval() on them?
EDIT: when i create a new sequence from scratch, it behaves as expected
Because of the usage of UpperCase characters, you have to use double quotes " around the identifiers:
SELECT currval('"VerbItalian_idVerbIt_seq"');

How to update multiple rows in SQL using a dict?

I have a huge dict of key/value pairs, something like
user_map = {
...
"A123": "A734",
"A342": "B322",
...
}
I have a table UserTable with two columns
+-----+---------+
| id | user_id |
+-----+---------+
| 1 | A123 |
| 2 | A372 |
| ... | ... |
+-----+---------+
I want to query the UserTable, and update the user_id if present in keys of user_map to its corresponding value. For example, in the table above, A123 will be updated to A734.
I am trying to do it in a single query.
for key in dictW:
x = dictW[key]
print(key, x)
This look gives you key and value separately so you can use them in your SQL statement. This will iterate through your dictionary so you can get a list of keys and values separately for your SQL statement
UPDATE tablename SET column = value WHERE row = number

Update a field in a JSON column in PostgreSQL

I have a work_item table that has the following schema
+---------+----------+---------------+
| id | data | data_type |
+------------------------------------+
| | | |
| | | |
| | | |
+---------+--------------------------+
and a document_type table with the following schema:
+---------+----------+
| id | name |
+--------------------+
| | |
| | |
| | |
+---------+-----------
The data column is a json column that has a Type field. This is a sample column data:
{"Id":"5d35a41f-3e91-4eda-819d-0f2d7c2ba55e","WorkItem":"24efa9ea-4291-4b0a-9623-e6122201fe4a","Type":"Tax Document","Date":"4/16/2009"}
I need to update data columns whose data_type column value is DocumentModel and Type field values matches a value in the name column of the document_type table to a json object containing the document_type id and the document_type name. Something like this {"id": "<doc_type_id>", name: "<doc_type_name>"}.
I tried to do this by executing this query:
UPDATE wf.work_item wi
SET data = jsonb_set(data::jsonb, '{Type}', (
SELECT jsonb_build_object('id', dt.id, 'name', dt.name)
FROM wf.document_type AS dt
WHERE wi.data ->> 'Type'::text = dt.name::text
), false)
WHERE wi.data_type = 'DocumentModel';
The above script runs without an error. However, what it does is something unwanted, it changes the data and data_type columns to null instead of updating the data column.
What is the issue with my script? Or can you suggest a better alternative to do a the desired update?
The problem arises when the document type is missing from the document_type table. Then jsonb_set() returns null (as the subquery does not give any results). A safer solution is to use the from clause in update:
update wf.work_item wi
set data = jsonb_set(
data::jsonb,
'{Type}',
jsonb_build_object('id', dt.id, 'name', dt.name),
false)
from wf.document_type as dt
where wi.data_type = 'DocumentModel'
and wi.data ->> 'Type'::text = dt.name::text;

How to insert same data (or copy) in a same table for different record in postgres

I have a table called Records which have data as following-
Section_one | section_two | values | cDate
name_Fit | hellp0 | present | 2014-08-23
name_Fit | onew | parcel | 2014-08-21
name_Fit | twow | new thing | 2014-07-04
Now I am trying to insert a new data called name_Fit_one w.r.t to column section_one which should have similar data (w.r.t column section_two and values) corresponding to it.
I tried to write the sql to do that but I am getting error on postgres. Can anyone please correct me what am I doing wrong?
Insert into records(section_one,section_two,values) Values ('name_Fit_one', select section_two,values from records where section_one='name_Fit');
Expected Results
Section_one | section_two | values | cDate
name_Fit | hellp0 | present | 2014-08-23
name_Fit | onew | parcel | 2014-08-21
name_Fit | twow | new thing | 2014-07-04
name_Fit_one| hellp0 | present | 2014-08-29
name_Fit_one| onew | parcel | 2014-08-29
name_Fit_one| twow | new thing | 2014-08-29
Need to write single query that would copy content of one record to another record?
Below is the sql fiddle for it-
http://sqlfiddle.com/#!2/9de58/1
If you're using postgresql you should be able to use:
Insert into records
select 'name_Fit_one', section_two, values, '2014-08-29'
from records
where section_one = 'name_Fit';
Fiddle: http://sqlfiddle.com/#!15/fbbed/1/0
Note that I renamed some of your columns as it was unclear whether your example above or your sqlfiddle contained your actual data and column names (they differ).
You can replace the literal '2014-08-29' with CURRENT_DATE if you want it to be the date in which you run the insert.
Insert into records (section_one,section_two, "values")
select 'name_Fit_one', section_two, "values"
from records
where section_one='name_Fit';
Note that values is a reserved word and thus needs to be quoted.

search a keyword in postgresql json field

I am currently using postgres 9.3.3
Following is how my table looks like -
Column | Type | Modifiers | Storage | Stats target | Description
--------------------+--------------------------+--------------------------------------------------------------------+----------+--------------+-------------
id | integer | not null default nextval('playerbase_palyerdata_id_seq'::regclass) | plain | |
date_joined | timestamp with time zone | not null | plain | |
belongs_to_camp_id | integer | not null | plain | |
belongs_to_coach_id | integer | not null | plain | |
json_kvps | character varying(2000) | not null | extended | |
One sample data is as follows -
id | date_joined | belongs_to_camp_id | belongs_to_coach_id | json_kvps
1 | 2014-03-07 18:10:45.824749+05:30 | 1 | 1 | {"alumnicode": "2003360009", "emailusername": "aaron#hotmail.com", "altemail": "", "salutation": "Mrs", "fname": "Aaron", "mname": "V", "lname": "Schwartz", "fullname": "Aaraon M Scwartz", "programmename": "MEP", "batchyearin": "2003"}
Now I want to search the entire table, and find a user with "emailusername":"aaron#hotmail.com"
As mentioned here -
http://www.postgresql.org/docs/current/static/functions-json.html
I try to write a query as follows -
SELECT * FROM playerbase_playerdata WHERE json_kvps->>'emailusername' = 'aaron#hotmail.com';
I was expecting a column as above, instead I got the following error -
ERROR: operator does not exist: character varying ->> unknown
LINE 1: ...ELECT * FROM memberbase_memberdata WHERE json_kvps->>'emailu...
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
Could someone please tell me, what am I missing?
Feature like this exists only from PostgreSQL release 9.3.
So, unfortunately, to make queries like this you need to update your PostgreSQL.
If you have 9.3, then you need use json column type.
Here you can see some example. It helps me, earlier:
http://clarkdave.net/2013/06/what-can-you-do-with-postgresql-and-json/
Have a nice day.
For PostgreSQL 9.3 or higher, one possible solution could be using a cast to json:
SELECT * FROM playerbase_playerdata WHERE json_kvps->>'emailusername' = 'aaron#hotmail.com';
SELECT * FROM playerbase_playerdata WHERE CAST(json_kvps AS JSON)->>'emailusername' = 'aaron#hotmail.com';
In case of django models you can use JsonField.
https://pypi.python.org/pypi/jsonfield.
It works fine, but if you'll use PostgreSQL version less than 9.2, it'll create character column.
select [Required Fields] from [Table Name] WHERE [Column Name]::text = '{[Key]}' ::text