Update PgSQL Self JOIN With Custom Values - postgresql

I'm trying to use UPDATE SELF JOIN and could not seem to get the correct SQL query.
Before the query, I execute this SQL query to get the values:
SELECT DISTINCT ON (purpose) purpose FROM user_assigned_customer
sales_manager
main_contact
representative
administrator
By the time I run this query, it overwrites all the purpose columns:
UPDATE user_assigned_customer SET purpose = (
SELECT 'main_supervisor' AS purpose FROM user_assigned_customer AS assigned_user
LEFT JOIN app_user ON app_user.id = assigned_user.app_user_id
WHERE app_user.role = 'supervisor'
AND user_assigned_customer.purpose IS NULL
AND assigned_user.id = user_assigned_customer.id
)
The purpose column is now only showing when running the first query:
main_supervisor
Wondering if there is a way to query to update SQL Self JOIN with a custom value.

I think I got it with a help of a friend.
UPDATE user_assigned_customer SET purpose = 'main_supervisor'
FROM user_assigned_customer AS assigned_user
LEFT JOIN app_user ON app_user.id = assigned_user.app_user_id
WHERE app_user.role = 'supervisor'
AND user_assigned_customer.purpose IS NULL
AND assigned_user.id = user_assigned_customer.id

Related

oracle merge query in postgres

I have this merge query in oracle and it was working fine. Now we are migrating to postgres 10 and trying to find equivalent for this in postgres.
MERGE INTO s.act_pack C USING((SELECT A.jid, A.pid, B.pcode,
B.mc, A.md, A.hd FROM s.act_pack A INNER JOIN s.act_pack B
ON A.pid = B.pid AND A.pcode = B.mc AND (A.hd <> B.hd
OR A.md<> B.md)) order by A.upd_ts desc) D ON(C.pid = D.pid AND
C.pcode = D.pcode AND C.jid = D.jid) WHEN MATCHED THEN UPDATE SET C.md =
D.md, C.hd= D.hd;
I see some forums on web says postgres doesnt support merge, and use INSERT ... ON CONFLICT
but with no background in postgres, I am not able to understand how this complex query can be written using that.
And some says postgres9.5 and above support merge statement. since we are using postgres 10 tried to use same oracle query in postgres but recieved ERROR: syntax error at or near "MERGE"
Any help is highly appreciated.
You don't need an "UPSERT" as you are not doing an INSERT, so a regular UPDATE is enough:
update act_pack C
SET C.md = D.md,
C.hd = D.h
from (
SELECT A.jid, A.pid, B.pcode, B.mc, A.md, A.hd
FROM s.act_pack A
INNER JOIN s.act_pack B
ON A.pid = B.pid
AND A.pcode = B.mc
AND (A.hd <> B.hd OR A.md<> B.md)
) d
where C.pid = D.pid
AND C.pcode = D.pcode
AND C.jid = D.jid
This is a direct "translation" of your code. But the fact that the same table is used three times is a bit strange. But without more information it's hard to know where exactly this could be made more efficient.

Update all using alias from an aggregate value derived from a Join

SELECT activities.id, max(symbols.bought_at) AS bought_at
FROM "activities"
JOIN holdings ON trackable_id = holdings.id AND trackable_type = 'Holding'
JOIN symbols on symbols.holding_id = holdings.id
GROUP BY activities.id"
I have a SQL that looks like the above. This works fine. However, I want to update all activities' created_at to the alias bought_at. I get an error that bought_at is not a column. Is it possible to do so in Postgres?
you can use that query as the source for an UPDATE statement:
update activities
set created_at = t.bought_at
from (
SELECT activities.id, max(symbols.bought_at) AS bought_at
FROM activities
JOIN holdings ON trackable_id = holdings.id AND trackable_type = 'Holding'
JOIN symbols on symbols.holding_id = holdings.id
GROUP BY activities.id
) t
where activities.id = t.id;
This assumes that activities.id is the primary key of that table.

Postgresql Update & Inner Join

I am trying to update data in Table: local.import_payments from Table: local.payments based on update and Inner Join queries. The query I used:
Update local.import_payments
Set local.import_payments.client_id = local.payments.payment_for_client__record_id,
local.import_payments.client_name = local.payments.payment_for_client__company_name,
local.import_payments.customer_id = local.payments.customer__record_id,
local.import_payments.customer_name = local.payment_from_customer,
local.import_payments.payment_id = local.payments.payment_id
From local.import_payments
Inner Join local.payments
Where local.payments.copy_to_imported_payments = 'true'
The client_id, client_name, customer_id, customer_name in the local.import_payments need to get updated with the values from the table local.payments based on the condition that the field copy_to_imported_payments is checked.
I am getting a syntax error while executing the query. I tried a couple of things, but they did not work. Can anyone look over the queries and let me know where the issue is
Try the following
UPDATE local.import_payments
Set local.import_payments.client_id =
local.payments.payment_for_client__record_id,
local.import_payments.client_name =
local.payments.payment_for_client__company_name,
local.import_payments.customer_id = local.payments.customer__record_id,
local.import_payments.customer_name = local.payment_from_customer,
local.import_payments.payment_id = local.payments.payment_id
FROM local.payments as lpay
WHERE lpay.<<field>> = local.import_payments.<<field>>
AND local.payments.copy_to_imported_payments = 'true'
You shouldn't to specify the schema/table for updated columns, only column names:
Do not include the table's name in the specification of a target column — for example, UPDATE table_name SET table_name.col = 1 is invalid.
from the doc
You shouldn't to use the updating table in the from clause except of the case of self-join.
You can to make your query shorter using "column-list syntax".
update local.import_payments as target
set (
client_id,
client_name,
customer_id,
customer_name,
payment_id) = (
source.payment_for_client__record_id,
source.payment_for_client__company_name,
source.customer__record_id,
source.payment_from_customer,
source.payment_id)
from local.payments as source
where
<join condition> and
source.copy_to_imported_payments = 'true'

Joining with set-returning function (SRF) and access columns in SQLAlchemy

Suppose I have an activity table and a subscription table. Each activity has an array of generic references to some other object, and each subscription has a single generic reference to some other object in the same set.
CREATE TABLE activity (
id serial primary key,
ob_refs UUID[] not null
);
CREATE TABLE subscription (
id UUID primary key,
ob_ref UUID,
subscribed boolean not null
);
I want to join with the set-returning function unnest so I can find the "deepest" matching subscription, something like this:
SELECT id
FROM (
SELECT DISTINCT ON (activity.id)
activity.id,
x.ob_ref, x.ob_depth,
subscription.subscribed IS NULL OR subscription.subscribed = TRUE
AS subscribed,
FROM activity
LEFT JOIN subscription
ON activity.ob_refs #> array[subscription.ob_ref]
LEFT JOIN unnest(activity.ob_refs)
WITH ORDINALITY AS x(ob_ref, ob_depth)
ON subscription.ob_ref = x.ob_ref
ORDER BY x.ob_depth DESC
) sub
WHERE subscribed = TRUE;
But I can't figure out how to do that second join and get access to the columns. I've tried creating a FromClause like this:
act_ref_t = (sa.select(
[sa.column('unnest', UUID).label('ob_ref'),
sa.column('ordinality', sa.Integer).label('ob_depth')],
from_obj=sa.func.unnest(Activity.ob_refs))
.suffix_with('WITH ORDINALITY')
.alias('act_ref_t'))
...
query = (query
.outerjoin(
act_ref_t,
Subscription.ob_ref == act_ref_t.c.ob_ref))
.order_by(activity.id, act_ref_t.ob_depth)
But that results in this SQL with another subquery:
LEFT OUTER JOIN (
SELECT unnest AS ob_ref, ordinality AS ref_i
FROM unnest(activity.ob_refs) WITH ORDINALITY
) AS act_ref_t
ON subscription.ob_refs #> ARRAY[act_ref_t.ob_ref]
... which fails because of the missing and unsupported LATERAL keyword:
There is an entry for table "activity", but it cannot be referenced from this part of the query.
So, how can I create a JOIN clause for this SRF without using a subquery? Or is there something else I'm missing?
Edit 1 Using sa.text with TextClause.columns instead of sa.select gets me a lot closer:
act_ref_t = (sa.sql.text(
"unnest(activity.ob_refs) WITH ORDINALITY")
.columns(sa.column('unnest', UUID),
sa.column('ordinality', sa.Integer))
.alias('act_ref'))
But the resulting SQL fails because it wraps the clause in parentheses:
LEFT OUTER JOIN (unnest(activity.ob_refs) WITH ORDINALITY)
AS act_ref ON subscription.ob_ref = act_ref.unnest
The error is syntax error at or near ")". Can I get TextAsFrom to not be wrapped in parentheses?
It turns out this is not directly supported by SA, but the correct behaviour can be achieved with a ColumnClause and a FunctionElement. First import this recipe as described by zzzeek in this SA issue. Then create a special unnest function that includes the WITH ORDINALITY modifier:
class unnest_func(ColumnFunction):
name = 'unnest'
column_names = ['unnest', 'ordinality']
#compiles(unnest_func)
def _compile_unnest_func(element, compiler, **kw):
return compiler.visit_function(element, **kw) + " WITH ORDINALITY"
You can then use it in joins, ordering, etc. like this:
act_ref = unnest_func(Activity.ob_refs)
query = (query
.add_columns(act_ref.c.unnest, act_ref.c.ordinality)
.outerjoin(act_ref, sa.true())
.outerjoin(Subscription, Subscription.ob_ref == act_ref.c.unnest)
.order_by(act_ref.c.ordinality.desc()))

PostgreSql: cannot use aggregate function in UPDATE

I have an Oracle query that I ported to PostgreSql:
UPDATE "SPD_PG"."TT_SPLDR_11A2F324_29"
SET "SECT_ORDER" = MAX("SECTIONS"."SECT_ORDER")+1 FROM "SPD_PG"."SECTIONS"
INNER JOIN "SPD_PG"."META_SECTIONS" ON ("SECTIONS"."META_SECT_ID"="META_SECTIONS"."META_SECT_ID")
WHERE ("META_SECTIONS"."META_SECT_ORDER"="TT_SPLDR_11A2F324_29"."META_SECT_ORDER"-1)
AND ("SECTIONS"."DOC_ID"="TT_SPLDR_11A2F324_29"."DOC_ID")
AND ("TT_SPLDR_11A2F324_29"."META_SECT_ORDER">0)
This give me: ERROR: cannot use aggregate function in UPDATE, seems PostgreSql doesn't support MAX in Update statements.
However if I rewrite the query as follows:
UPDATE "SPD_PG"."TT_SPLDR_11A2F324_29"
SET "SECT_ORDER" = "MAX_VALUE" FROM (
SELECT MAX("SECTIONS"."SECT_ORDER")+1 AS "MAX_VALUE" FROM "SPD_PG"."SECTIONS"
INNER JOIN "SPD_PG"."META_SECTIONS" ON ("SECTIONS"."META_SECT_ID"="META_SECTIONS"."META_SECT_ID")
WHERE ("META_SECTIONS"."META_SECT_ORDER"="TT_SPLDR_11A2F324_29"."META_SECT_ORDER"-1)
AND ("SECTIONS"."DOC_ID"="TT_SPLDR_11A2F324_29"."DOC_ID")
AND ("TT_SPLDR_11A2F324_29"."META_SECT_ORDER">0)
) "TBL_ALIAS"
it says ERROR: subquery in FROM cannot refer to other relations of same query level.
So I can't figure out how to write this query.
Try this:
UPDATE "SPD_PG"."TT_SPLDR_11A2F324_29"
SET "SECT_ORDER" = (SELECT MAX("SECTIONS"."SECT_ORDER")+1
FROM "SPD_PG"."SECTIONS"
INNER JOIN "SPD_PG"."META_SECTIONS" ON ("SECTIONS"."META_SECT_ID"="META_SECTIONS"."META_SECT_ID")
WHERE ("META_SECTIONS"."META_SECT_ORDER"="TT_SPLDR_11A2F324_29"."META_SECT_ORDER"-1)
AND ("SECTIONS"."DOC_ID"="TT_SPLDR_11A2F324_29"."DOC_ID")
AND ("TT_SPLDR_11A2F324_29"."META_SECT_ORDER">0)
)