How to update multiple rows in SQL using a dict? - sql

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

Related

PostgreSQL query to select records which a specific value doesn't include in text array

I have a table like this
| id | data |
|---------------|---------------------|
| org:abc:basic | {org,org:abc:basic} |
| org:xyz:basic | {org,basic} |
| org:efg:basic | {org} |
I need to write a query to select all the rows which doesn't have the id inside the data column.
Or at least I need to query all the records which doesn't have a text starting from org: and ending with :basic within data.
Currently for this I try to run
SELECT * FROM t_permission WHERE 'org:%:basic' NOT LIKE ANY (data)
query which returns everything even the first row.
you can use the <> operator with ALL against the array:
select *
from the_table
where id <> all(data);

Sort SQL results and include missing keys

I have a Postgres table like this (greatly simplified):
id | object_id (foreign id) | key (text) | value (text)
1 | 1 | A | 0foo
2 | 1 | B | 1bar
3 | 1 | C | 2baz
4 | 1 | D | 3ham
5 | 2 | C | 4sam
6 | 3 | F | 5pam
…
(billions of rows)
I select object_ids according to some query (not relevant here), and then sort them according to the value of a specified key.
def sort_query_result(query, sort_by, limit, offset):
return query\
.with_entities(Table.object_id)\
.filter(Table.key == sort_by)\
.order_by(desc(Table.value))\
.limit(limit).offset(offset).subquery()
For example, assume a query matches object_ids 1 and 2 above. When sort_by=C, I want the result to be returned in the order [2, 1], because 4sam > 2baz.
This works well but there's one big problem:
Object ids that are returned by query but do not have any row for the sort_by key, are not returned at all.
For example, for a query that matches object_ids 1 and 2, sort_query_results(query, sort_by='D') == [1]. The object_id 2 is dropped because it has no D, which is undesirable.
Instead, I'd like to return all object_ids from the query. Those without the sort key should be sorted at the end, in any order: sort_query_results(query, sort_by='D') == [1, 2].
What's the best way to achieve that?
Note: I do not have the freedom to change the DB schema or business logic. But I can change the query code. I use SQLAlchemy ORM from Python, but could execute raw Postgres commands if necessary. Thank you.

PostgreSQL add new not null column and fill with ids from insert statement

I´ve got 2 tables.
CREATE TABLE content (
id bigserial NOT NULL,
name text
);
CREATE TABLE data (
id bigserial NOT NULL,
...
);
The tables are already filled with a lot of data.
Now I want to add a new column content_id (NOT NULL) to the data table.
It should be a foreign key to the content table.
Is it possible to automatically create an entry in the content table to set a content_id in the data table.
For example
**content**
| id | name |
| 1 | abc |
| 2 | cde |
data
| id |... |
| 1 |... |
| 2 |... |
| 3 |... |
Now I need an update statement that creates 3 (in this example) content entries and add the ids to the data table to get this result:
content
| id | name |
| 1 | abc |
| 2 | cde |
| 3 | ... |
| 4 | ... |
| 5 | ... |
data
| id |... | content_id |
| 1 |... | 3 |
| 2 |... | 4 |
| 3 |... | 5 |
demo:db<>fiddle
According to the answers presented here: How can I add a column that doesn't allow nulls in a Postgresql database?, there are several ways of adding a new NOT NULL column and fill this directly.
Basicly there are 3 steps. Choose the best fitting (with or without transaction, setting a default value first and remove after, leave the NOT NULL contraint first and add afterwards, ...)
Step 1: Adding new column (without NOT NULL constraint, because the values of the new column values are not available at this point)
ALTER TABLE data ADD COLUMN content_id integer;
Step 2: Inserting the data into both tables in a row:
WITH inserted AS ( -- 1
INSERT INTO content
SELECT
generate_series(
(SELECT MAX(id) + 1 FROM content),
(SELECT MAX(id) FROM content) + (SELECT COUNT(*) FROM data)
),
'dummy text'
RETURNING id
), matched AS ( -- 2
SELECT
d.id AS data_id,
i.id AS content_id
FROM (
SELECT
id,
row_number() OVER ()
FROM data
) d
JOIN (
SELECT
id,
row_number() OVER ()
FROM inserted
) i ON i.row_number = d.row_number
) -- 3
UPDATE data d
SET content_id = s.content_id
FROM (
SELECT * FROM matched
) s
WHERE d.id = s.data_id;
Executing several statements one after another by using the results of the previous one can be achieved using WITH clauses (CTEs):
Insert data into content table: This generates an integer series starting at the MAX() + 1 value of the current content's id values and has as many records as the data table. Afterwards the new ids are returned
Now we need to match the current records of the data table with the new ids. So for both sides, we use row_number() window function to generate a consecutive row count for each records. Because both, the insert result and the actual data table have the same number of records, this can be used as join criterion. So we can match the id column of the data table with the new content's id values
This matched data can used in the final update of the new content_id column
Step 3: Add the NOT NULL constraint
ALTER TABLE data ALTER COLUMN content_id SET NOT NULL;

Do UPSERT based on specific value of JSON in Postgres 10

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')
;

Check if one json value is in the json value from another JSON, for each key

With SQL in postgres, I want to know if one JSON is 'IN' another JSON.
For example:
json_1 = {"a": ["123"], "b": ["456", "789"]}
json_2 = {"a": ["123"], "b": ["456"]}
In the above case, json_2["a"] is in json_1["a"] and json_2["b"] is in json_1["b"].
If I would know all the possible keys of the json, I would easily be able to write the above per key. However, the problem is that I don't know how many and which keys are in the JSON. How can I check for every key in the JSON, if json_2 is in json_1?
I'm not sure about what output format you want, but this will create one row per key, with a boolean stating whether the json_2 key's array values are contained within the json_1 key's values.
CREATE TABLE t (json_1 JSONB, json_2 JSONB);
INSERT INTO t
VALUES
('{"a":["123"],"b":["456","789","aaa"],"c":["999"],"d":[]}',
'{"a":["123"],"b":["789","456"],"c":["123"],"d":["x"]}');
Query #1
SELECT key, value <# (json_1->key) AS contained
FROM (
SELECT (JSONB_EACH(json_2)).*, json_1
FROM t
) j;
Returns:
| key | contained |
| --- | --------- |
| a | true |
| b | true |
| c | false |
| d | false |
View on DB Fiddle