Delete using left outer join in Postgres - postgresql

I am switching a database from MySQL to Postgres SQL. A select query that worked in MySQL works in Postgres but a similar delete query does not.
I have two tables of data which list where certain back-up files are located. Existing data (ed) and new data (nd). This syntax will pick out existing data which might state where a file is located in the existing data table, matching it against equal filename and path, but no information as to where it is located in the new data:
SELECT ed.id, ed.file_name, ed.cd_name, ed.path, nd.cd_name
FROM tv_episodes AS ed
LEFT OUTER JOIN data AS nd ON
ed.file_name = nd.file_name AND
ed.path = nd.path
WHERE ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL;
I wish to run a delete query using this syntax:
DELETE ed
FROM tv_episodes AS ed
LEFT OUTER JOIN data AS nd ON
ed.file_name = nd.file_name AND
ed.path = nd.path
WHERE ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL;
I have tried DELETE ed and DELETE ed.* both of which render syntax error at or near "ed". Similar errors if I try without the alias of ed. If I attempt
DELETE FROM tv_episodes AS ed
LEFT JOIN data AS nd.....
Postgres sends back syntax error at or near "LEFT".
I'm stumped and can't find much on delete queries using joins specific to psql.

As others have noted, you can't LEFT JOIN directly in a DELETE statement. You can, however, self join on a primary key to the target table with a USING statement, then left join against that self-joined table.
DELETE FROM tv_episodes
USING tv_episodes AS ed
LEFT OUTER JOIN data AS nd ON
ed.file_name = nd.file_name AND
ed.path = nd.path
WHERE
tv_episodes.id = ed.id AND
ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL;
Note the self join on tv_episodes.id in the WHERE clause. This avoids the sub-query route provided above.

As bf2020 points out, postgres does not support JOINs when conducting a DELETE query. The proposed solution of a sub-query made me think of the solution. Refine the SELECT query from above and employ it as a sub-query to a DELETE query statement:
DELETE FROM tv_episodes
WHERE id in (
SELECT ed.id
FROM tv_episodes AS ed
LEFT OUTER JOIN data AS nd ON
ed.file_name = nd.file_name AND
ed.path = nd.path
WHERE ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL
);
Sub-queries can often be inefficient consuming time and CPU resources with some database systems, especially MySQL. From my experience I try to avoid using a sub-query due to that inefficiency plus that such queries are sometimes an easy way out to honing one's skill like learning JOIN syntax.
Since postgre does not permit delete queries using join, the above is the solution that works.

Use the DELETE... USING syntax:
DELETE FROM tv_episodes USING data WHERE
tv_episodes.file_name = data.file_name AND
tv_episodes.path = data.path AND
tv_episodes.cd_name = 'MediaLibraryDrive' AND
data.cd_name IS NULL;

Instead of
DELETE ed
FROM tv_episodes AS ed
LEFT OUTER JOIN data AS nd ON
ed.file_name = nd.file_name AND
ed.path = nd.path
WHERE ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL;
please try
DELETE FROM tv_episodes
WHERE cd_name = 'MediaLibraryDrive' AND
(tv_episodes.filename, tv_episodes.path IN
(SELECT ed.filename,
ed.path
FROM tv_episodes AS ed
INNER JOIN data AS nd
ON ed.file_name = nd.file_name
AND ed.path = nd.path
WHERE nd.cd_name IS NULL)
)
;
JOIN is not valid in a DELETE query according to the postgresql documentation. You might need to concatenate the left and right parts of the IN expression.

Related

Is there a way to modify this query and make it more simple?

I am working on a MySQl data base and have several tables that I need to join based on a temporary table that I created (layer_2).
Each table that I join has the join key for the next table;
E.g. layer_2 joins with "colegios" table, then the result will have the join key for the next table "ciudades". and finally the result will have the Join key for the next table "departamentos".
from what I got it works but I want to know if there is a way to simplify this?
Select
cuestionario_id,
tiempo,
nro_preguntas,
nro_preguntas_correctas,
tipo_usuario,
fecha_creacion_cuestionario,
con_tiempo,
det_cuestionarios_id,
respuesta_seleccionada,
pregunta_id,
enunciado,
respuesta_correcta,
usuarios_id,
dominio_correo,
tipo_institucion,
materia,
tematica,
area,
nombre_colegios,
direccion_colegios,
nombre_ciudades,
redsaber.departamentos.nombre as nombre_departamentos
From
(
Select
cuestionario_id,
tiempo,
nro_preguntas,
nro_preguntas_correctas,
cuestionable_id,
tipo_usuario,
fecha_creacion_cuestionario,
con_tiempo,
det_cuestionarios_id,
respuesta_seleccionada,
pregunta_id,
enunciado,
respuesta_correcta,
usuarios_id,
dominio_correo,
cole_ciud_id,
tipo_institucion,
materia,
tematica,
area,
combine_ciudad_id,
nombre_colegios,
direccion_colegios,
redsaber.ciudades.nombre as nombre_ciudades,
departamento_id
From
(Select
cuestionario_id,
tiempo,
nro_preguntas,
nro_preguntas_correctas,
cuestionable_id,
tipo_usuario,
fecha_creacion_cuestionario,
con_tiempo,
det_cuestionarios_id,
respuesta_seleccionada,
pregunta_id,
enunciado,
respuesta_correcta,
usuarios_id,
dominio_correo,
cole_ciud_id,
tipo_institucion,
materia,
tematica,
area,
ifnull(redsaber.colegios.ciudad_id,cole_ciud_id) as combine_ciudad_id,
redsaber.colegios.nombre as nombre_colegios,
redsaber.colegios.direccion as direccion_colegios
From layer_2
Left Join redsaber.colegios on layer_2.cole_ciud_id = redsaber.colegios.id
AND tipo_institucion = 'Colegio') as layer_3
Left Join redsaber.ciudades on combine_ciudad_id = redsaber.ciudades.id) as layer_4
Left Join redsaber.departamentos on departamento_id = redsaber.departamentos.id

Update PgSQL Self JOIN With Custom Values

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

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.

Copy join-query from database to another datable

Previously, I am using dblink to achieve the mission but it involved copy one query only. What if I have doing the join query (4 tables) in one database, then i want to copy the data output into another database.Anyone know about it ?
select
a.sysname, a.ip, b.host_id, b.resource_name, b.resource_id
, c.metric_id, d.metric_name, c.value, c.resource_id
, to_timestamp(c.date_id)as datetime
from inv.el a
inner join inv.if b on a.host_id = b.host_id
inner join me.me_cr c on b.resource_id = c.resource_id
inner join inv.me d on c.metric_id = d.metric_id
where date_id = (
select max(date_id) from me.me_cr
)
you can try using postgres_fdw on Release > 9.6 as it
... now supports remote joins...
https://www.postgresql.org/docs/9.6/static/release-9-6.html

Is it possible to rename a table in Redshift without propagating the change to dependent views?

I noticed that if you rename a table in Redshift the new name is automatically progagated to all the views that are based on that table.
Is there a way to prevent this behavior so that the view definition would contain the old table name?
Looks like this feature was introduced to Redshift. See WITH NO SCHEMA BINDING in the Redshift docs.
CREATE VIEW i_am_view_are_you AS
SELECT good_column
FROM public.i_am_table
WITH NO SCHEMA BINDING
Mihai,
That's an excellent question.
The feature that you are searching would be very useful when you are manually doing table maintenance and you want to move/create/rename tables without impacting existing dependent objects.
The short answer to your question is "NO". There is NOT a way to prevent this behavior in Redshift.
There is nothing you can do either on your "ALTER TABLE" statement or "CREATE VIEW" statement that would prevent a dependent view from being changed when the underlying table changes.
BUT
You can easily obtain the DDL for the views before changing the name, and you can re-run the DDL when you are ready, so your views would point the the right place.
The following SQL would show all the dependent views for a particular table and produce to re-create each corresponding view.
SELECT DISTINCT
srcobj.oid AS src_oid
,srcnsp.nspname AS src_schemaname
,srcobj.relname AS src_objectname
,tgtobj.oid AS dependent_viewoid
,tgtnsp.nspname AS dependent_schemaname
,tgtobj.relname AS dependent_objectname
,'--DROP VIEW ' + tgtnsp.nspname + '.' + tgtobj.relname + ';\nCREATE OR REPLACE VIEW ' + tgtnsp.nspname+ '.' + tgtobj.relname + ' AS\n' + COALESCE(pg_get_viewdef(tgtobj.oid, TRUE), '') AS ddl
FROM
pg_catalog.pg_class AS srcobj
INNER JOIN
pg_catalog.pg_depend AS srcdep
ON srcobj.oid = srcdep.refobjid
INNER JOIN
pg_catalog.pg_depend AS tgtdep
ON srcdep.objid = tgtdep.objid
JOIN
pg_catalog.pg_class AS tgtobj
ON tgtdep.refobjid = tgtobj.oid
AND srcobj.oid <> tgtobj.oid
LEFT OUTER JOIN
pg_catalog.pg_namespace AS srcnsp
ON srcobj.relnamespace = srcnsp.oid
LEFT OUTER JOIN
pg_catalog.pg_namespace tgtnsp
ON tgtobj.relnamespace = tgtnsp.oid
WHERE tgtdep.deptype = 'i' --dependency_internal
AND tgtobj.relkind = 'v'
AND srcobj.relname = '<your-table-name>'
AND srcnsp.nspname = ' <your-schema-name>' ;
Substitute and for your corresponding values.