PostgreSQL join:删除一个表中存在的记录,但不删除另一个表中的记录

时间:2011-04-06 08:48:40

标签: postgresql join sql-delete

我有一个嵌入Flash游戏的Drupal网站。

已注册的网站用户列在 drupal_users 表中 - 此处列出了一周前注册的

# select uid, created from drupal_users where 
      to_timestamp(created) < (now() - interval '7 days') limit 5;
 uid  |  created
------+------------
 9903 | 1300257067
 9904 | 1300259929
 9750 | 1299858284
 9751 | 1299858603
 8083 | 1285514989
(5 rows)

Flash 游戏用户列在另一个表格中 - pref_users ,并在其ID前加上“DE”字符串:

# select id from pref_users where id like 'DE%' limit 5;
   id
--------
 DE9054
 DE9055
 DE9056
 DE9057
 DE9058
(5 rows)

我想摆脱一周前在我网站注册的(可能是垃圾邮件机器人)用户,但仍然没有玩过Flash游戏。即我想删除drfal_users记录,这些记录在pref_users表中不存在

与此同时,我不想做类似的事情:

# delete from drupal_users where 
    to_timestamp(created) < (now() - interval '7 days') and
    'DE'||uid not in (select id from pref_users where id like 'DE%');

因为我不确定,上面的select语句有多大(可能有一个限制?我正在使用PostgreSQL 8.4.7和CentOS 5.5 / 64位。在Drupal7之前我使用的是phpBB3,有时候我从phpBB3管理控制台删除旧的论坛帖子时看到这种SQL语句失败了。

所以我的问题是,如果上述陈述可以改写为some kind of SQL-join

4 个答案:

答案 0 :(得分:3)

在处理具有数百万条记录的表的联接时,使用NOT IN无法获得可接受的性能。 相反,我写了相当于:

alter table drupal_users add column dont_delete boolean;

然后

update drupal_users set dont_delete = true from pref_users 
where 'DE'||drupal_users.uid = pref_users.id.

一旦创建了新的drupal_users,这将不再有效,但由于您只删除了超过7天的记录,所以没关系。 最后,验证您的记录并发出:

delete from drupal_users where dont_delete is null
  and to_timestamp(drupal_users.created) < (now() - interval '7 days');

清理:

alter table drupal_users drop column dont_delete;

答案 1 :(得分:1)

无法将删除重写为SQL连接,AFAIK。 但你为什么不喜欢

delete from drupal_users where 
to_timestamp(created) < (now() - interval '7 days') and
'DE'||uid not in (select id from pref_users where id like 'DE%');

这个语句的大小是静态的(你这里不生成任何动态SQL),所以这是一个非常有效的方法,应该运行得非常快(如果这是你所关心的)。

答案 2 :(得分:0)

我重新创建了你说有一些postgresql限制的场景:

create table t0 (id int primary key);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "t0_pkey" for table "t0"
CREATE TABLE

create table t1 (id int primary key);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1"
CREATE TABLE

insert into t0 (id) 
select * from generate_series(1, 100000, 2);
INSERT 0 50000

insert into t1 (id) 
select * from generate_series(2, 100000, 2);
INSERT 0 50000

select * from t0 order by id limit 3;
 id 
----
  1
  3
  5
(3 rows)

select * from t1 order by id limit 3;
 id 
----
  2
  4
  6
(3 rows)

现在我删除t0中t1中不存在的所有行(所有这些行):

delete from t0
where id not in (select id from t1);

它有效

答案 3 :(得分:0)

这是使用EXISTS子查询执行此操作的另一种方法:

delete from drupal_users D
where to_timestamp(created) < (now() - interval '7 days')
and not exists (select 1 from pref_users P where P.id = 'DE' || D.uid);