0

我的查询删除了整个表而不是重复的行。视频为证:https ://streamable.com/3s843

create table customer_info (
    id INT,
    first_name VARCHAR(50),
    last_name VARCHAR(50),
    phone_number VARCHAR(50)
);
insert into customer_info (id, first_name, last_name, phone_number) values
(1, 'Kevin', 'Binley', '600-449-1059'),
(1, 'Kevin', 'Binley', '600-449-1059'),
(2, 'Skippy', 'Lam', '779-278-0889');

我的查询:

with t1 as (
select *, row_number() over(partition by id order by id) as rn
from customer_info)

delete
from customer_info 
where id in (select id from t1 where rn > 1);
4

2 回答 2

1

您的查询将从每组欺骗中删除所有id行(因为所有行都共享您选择的相同 - 这就是@wildplasser 用微妙的评论暗示的内容)并且只有最初唯一的行才能生存。因此,如果它“删除整个表”,则意味着根本没有唯一的行。

在您的查询中,欺骗是由(id)单独定义的,而不是由标题所暗示的整行定义。

无论哪种方式,都有一个非常简单的解决方案:

DELETE FROM customer_info c
WHERE  EXISTS (
   SELECT FROM customer_info c1
   WHERE  ctid < c.ctid
   AND    c1 = c  -- comparing whole rows
   );

由于您处理的是完全相同的行,因此区分它们的剩余方法是内部元组 ID ctid

我的查询删除了所有行,其中存在具有较小行的相同行ctid。因此,只有每组骗子中的“第一”行幸存下来。

值得注意的是,在这种情况下, NULL比较相等- 这很可能是所希望的。手册:

如果结果取决于比较两个 NULL 值或一个 NULL 和一个非 NULL,则 SQL 规范要求逐行比较返回 NULL。PostgreSQL 仅在比较两个行构造函数的结果(如第9.23.5节)或将行构造函数与子查询的输出(如第 9.22节)进行比较时才会这样做。在比较两个复合类型值的其他上下文中,两个 NULL 字段值被认为是相等的,[...]

如果欺骗是由id单独定义的(如您的查询所示),那么这将起作用:

DELETE FROM customer_info c
WHERE  EXISTS (
   SELECT FROM customer_info c1
   WHERE  ctid < c.ctid
   AND    id = c.id
   );

但是,可能有更好的方法来决定保留哪些行,而不是ctid作为最后手段!

显然,您将添加一个PRIMARY KEY以避免重新出现最初的困境。对于第二种解释,id是候补。

有关的:

关于ctid

于 2019-08-19T00:25:31.923 回答
0

如果表没有键,则不能。

表具有唯一标识每一行的“键”。如果您的表没有任何键,那么您将无法从另一行中识别出一行。

我能想到的删除重复行的唯一解决方法是:

  1. 在桌子上添加一把钥匙。
  2. 使用 键删除多余的行。

例如:

create sequence seq1;
alter table customer_info add column k1 int;
update customer_info set k1 = nextval('seq1');

delete from customer_info where k1 in (
  select k1 
  from (
    select
      k1,
      row_number() over(partition by id, first_name, last_name, phone_number) as rn
    from customer_info
  ) x
  where rn > 1
) 

现在你只有两行。

于 2019-08-19T01:42:17.677 回答