我有一个嵌入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?
答案 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);