简体   繁体   中英

How to delete records from CTE (Common Table Expression) in Postgres

I have written a following query using CTE in Postgres. Now I am unable to delete records from it.

WITH cte AS (
    SELECT 
   
        firstname, 
        lastname, 
        country, 
        ROW_NUMBER() OVER (
            PARTITION BY 
                firstname, 
                lastname, 
                country
        ) row_num
     FROM 
        employee
) 
delete  from cte
where row_num >1

When I run this query it show me error:

relation "cte" does not exist

Here is sample of my table 'employee'

id  firstname  lastname  country
1   "Raj"      "Gupta"   "India"
2   "Raj"      "Gupta"   "India"
3   "Mohan"    "Kumar"   "USA"
4   "James"    "Barry"   "UK"
5   "James"    "Barry"   "UK"
6   "James"    "Barry"   "UK"

Deleting from a CTE is not possible.

You can do like this:

demo:db<>fiddle

DELETE FROM employee
WHERE id IN (
    SELECT
        id
    FROM (
        SELECT
            id,
            ROW_NUMBER() OVER (PARTITION BY firstname, lastname, country) row_num
        FROM 
            employee
    ) s
    WHERE row_num > 1
)

If you want to use a CTE nevertheless, you can move the subquery into one:

demo:db<>fiddle

WITH cte AS (
    SELECT
        id
    FROM (
        SELECT
            id,
            ROW_NUMBER() OVER (PARTITION BY firstname, lastname, country) row_num
        FROM 
            employee
    ) s
    WHERE row_num > 1
)
DELETE FROM employee
WHERE id IN (SELECT * FROM cte)

You can not delete record from CTE table in postgresql. Although it is possible in other DB server like SQLSERVER. You can see CTE document for postgresql.

I think better case will be just to filter not needed rows on step of creating CTE (without any deleting)

WITH cte AS (
    SELECT 
        firstname, 
        lastname, 
        country, 
        ROW_NUMBER() OVER (
            PARTITION BY 
                firstname, 
                lastname, 
                country
        ) row_num
     FROM employee
     WHERE row_num >1
)

I would suggest using:

delete from employee e
    where e.id > (select min(e2.id)
                  from employee e2
                  where e2.firstname = e.firstname and
                        e2.lastname = e.lastname and 
                        e2.country = e.country
                 );

This is standard SQL and will work in both SQL Server and Postgres.

The only difference from the SQL Server version is how NULL values are treated. This treats them as all being "different" so a matching NULL values would not be considered a match. This is probably not an issue in your data, but you can use:

                  where e2.firstname is not distinct from e.firstname and
                        e2.lastname is not distinct from e.lastname and 
                        e2.country is not distinct from e.country

If you want NULL values to match.

Note that the original version should be able to make use of an index on (lastname, firstname, country, id) (in either database).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM