Оператор SQL для разделения таблицы на основе объединения - PullRequest
1 голос
/ 20 марта 2010

У меня есть первичная таблица для Статей, которая связана таблицей объединения Информация с таблицей Теги, в которых есть только небольшое количество записей. Я хочу разделить таблицу «Статьи», удалив строки или создав новую таблицу, содержащую только те записи, которые мне нужны, исходя из отсутствия ссылки на определенный тег. Есть несколько миллионов статей. Как я могу это сделать?

Не во всех статьях вообще есть какие-либо теги, а в некоторых есть много тегов.

Пример:

table Articles
  primary_key id
table Info
  foreign_key article_id
  foreign_key tag_id
table Tags
  primary_key id

Мне было легко отделить статьи, у которых действительно есть совпадение, поэтому я подумал, что, возможно, я мог бы сделать это, а затем использовать оператор NOT IN, но это так медленно, что неясно, будет ли это Конец. Я сделал это с помощью этих команд:

INSERT INTO matched_articles SELECT * FROM articles a LEFT JOIN info i ON a.id = i.article_id WHERE i.tag_id = 5;
INSERT INTO unmatched_articles SELECT * FROM articles a WHERE a.id NOT IN (SELECT m.id FROM matched_articles m);

Если это имеет значение, я на Postgres.

Ответы [ 3 ]

1 голос
/ 20 марта 2010
INSERT INTO matched_articles 
SELECT * FROM articles a LEFT JOIN info i ON a.id = i.article_id WHERE i.tag_id = 5; 

INSERT INTO unmatched_articles 
SELECT * FROM articles a WHERE a.id NOT IN (SELECT m.id FROM matched_articles m); 

Здесь так много неправильного, я не уверен, с чего начать. ОК в вашей первой вставке вам не нужно левое соединение, на самом деле у вас его нет. Должно быть

INSERT INTO matched_articles 
SELECT * FROM articles a INNER JOIN info i ON a.id = i.article_id WHERE i.tag_id = 5; 

Если бы вам понадобилось левое соединение, у вас было бы

INSERT INTO matched_articles 
SELECT * FROM articles a LEFT JOIN info i ON a.id = i.article_id AND i.tag_id = 5; 

Когда вы помещаете что-то с правой стороны левого соединения в предложение where (кроме поиска нулевых значений), то вы конвертируете это во внутреннее соединение, потому что оно должно соответствовать этому условию, поэтому записи, которые не совпадение в правой таблице исключено.

Теперь второе утверждение можно сделать с помощью специального случая левого соединения, хотя то, что у вас есть, будет работать.

INSERT INTO matched_articles 
SELECT * FROM articles a 
LEFT JOIN info i ON a.id = i.article_id AND i.tag_id = 5
WHERE i.tag_id is null

Это даст вам все записи в информационной таблице, кроме тех, которые соответствуют таблице статей.

Теперь следующее, вы не должны писать вставные элементы без указания полей, которые вы хотите вставить. Также вы никогда не должны писать оператор выбора, используя select *, особенно если у вас есть соединение. Это обычно небрежное, ленивое кодирование и должно быть исправлено. Что если кто-то изменит структуру одной из таблиц, но не другой? Такие вещи плохи для обслуживания, и в случае выбора статистики с объединением, он возвращает столбец дважды (столбец объединения), а это пустая трата ресурсов сервера и сети. Это просто плохое кодирование, чтобы лень указывать, что вам нужно и только то, что вам нужно. Так что избавьтесь от привычки и не делайте этого снова для любого производственного кода.

Если ваша текущая позиция слишком медленная, вы также можете исправить ее с помощью правильных индексов. Индексируются ли поля идентификаторов в обеих таблицах? С другой стороны, если есть миллионы статей, потребуется время, чтобы вставить их. Часто лучше делать это партиями, может быть, 50000 за раз (еще меньше, если это занимает слишком много времени). Просто выполните цикл вставки ina, который выбирает верхние записи XXX, а затем зацикливается до тех пор, пока не будет затронуто число строк.

1 голос
/ 20 марта 2010

Не уверен, если у Postgres есть концепция временной таблицы.
Вот как это можно сделать.

CREATE Table #temp
AS SELECT A.ID, COUNT(i.*) AS Total
FROM Articles A
LEFT JOIN info i
ON A.id = i.Article_ID AND i.Tag_ID = 5
GROUP BY A.ID

INSERT INTO Matched_Articles
SELECT A.*
FROM Articles A INNER JOIN #temp t
ON A.ID = t.Article_ID AND T.Total = 0

DELETE FROM #Temp
WHERE Total = 0

INSERT INTO UnMatched_Articles
SELECT A.*
FROM Articles AINNER JOIN #temp t
ON A.ID = t.Article_ID

Обратите внимание, что я не использую никакой редактор, чтобы попробовать это.
Надеюсь, это даст вам подсказку о том, как бы я подошел к этому.

1 голос
/ 20 марта 2010

Ваши запросы выглядят нормально, за исключением того, что первый должен быть внутренним, а не левым. Если вы хотите попробовать что-то еще, подумайте:

INSERT INTO matched_articles 
SELECT * 
FROM articles a 
INNER JOIN info i ON a.id = i.article_id 
WHERE i.tag_id = 5;

INSERT INTO unmatched_articles 
SELECT * 
FROM articles a 
LEFT JOIN info i ON a.id = i.article_id AND a.id <> 5
WHERE a.id IS NULL

Это может быть быстрее, но на самом деле, то, что у вас есть, вероятно, хорошо, если вам нужно сделать это только один раз.

...