Присоединение к PostgreSQL: удаляет записи, присутствующие в одной таблице, но не в другой - PullRequest
3 голосов
/ 06 апреля 2011

У меня есть сайт Drupal, в который встроена Flash-игра.

Зарегистрированные пользователи веб-сайта перечислены в таблице 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 и к их идентификатору добавлена ​​строка "DE":

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

Я хотел бы избавиться от (возможно, от спам-роботов) пользователей, которые зарегистрировались на моем сайте более недели назад, но еще не играли во флэш-игру. То есть Я бы хотел удалить записи drupal_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 bit. До Drupal7 я использовал phpBB3 и иногда Я видел сбой такого рода SQL-операторов при удалении старых сообщений форума из консоли администратора phpBB3).

Так что мой вопрос: можно ли переписать приведенное выше утверждение как какое-то SQL-соединение ?

Ответы [ 4 ]

3 голосов
/ 26 сентября 2011

Я не смог получить приемлемую производительность, используя 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 голос
/ 06 апреля 2011

Перезаписать удаление как соединение 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), так что это совершенно правильный подход, и он должен выполняться довольно быстро (еслиэто то, что тебя беспокоит).

0 голосов
/ 08 апреля 2011

Вот еще один способ сделать это, используя подзапрос 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);
0 голосов
/ 06 апреля 2011

Я воссоздал сценарий, где вы говорите, что есть некоторый предел 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);

И это работает

...