Вставить или игнорировать в триггере - PullRequest
4 голосов
/ 30 апреля 2010

У меня есть база данных (для отслеживания статистики электронной почты), которая выросла до сотен мегабайт, и я искал способы уменьшить ее.

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

CREATE TABLE AddressLookup (
   ID      INTEGER PRIMARY KEY AUTOINCREMENT,
   Address TEXT UNIQUE
);

CREATE TABLE EmailInfo (
   MessageID   INTEGER PRIMARY KEY AUTOINCREMENT,
   ToAddrRef   INTEGER REFERENCES AddressLookup(ID),
   FromAddrRef INTEGER REFERENCES AddressLookup(ID)
   /* Additional columns omitted for brevity. */
);

А для удобства можно объединить следующие таблицы:

CREATE VIEW EmailView AS
SELECT
   MessageID,
   A1.Address AS ToAddr,
   A2.Address AS FromAddr
FROM EmailInfo
   LEFT JOIN AddressLookup A1 ON (ToAddrRef = A1.ID)
   LEFT JOIN AddressLookup A2 ON (FromAddrRef = A2.ID);

Чтобы использовать это представление как обычную таблицу, я сделал несколько триггеров:

CREATE TRIGGER trg_id_EmailView
   INSTEAD OF DELETE ON EmailView
BEGIN
   DELETE FROM EmailInfo WHERE MessageID = OLD.MessageID;
END;

CREATE TRIGGER trg_ii_EmailView
   INSTEAD OF INSERT ON EmailView
BEGIN
   INSERT OR IGNORE INTO AddressLookup(Address) VALUES (NEW.ToAddr);
   INSERT OR IGNORE INTO AddressLookup(Address) VALUES (NEW.FromAddr);
   INSERT INTO EmailInfo
      SELECT NEW.MessageID, A1.ID, A2.ID
      FROM AddressLookup A1, AddressLookup A2
      WHERE A1.Address = NEW.ToAddr AND A2.Address = NEW.FromAddr;
END;

CREATE TRIGGER trg_iu_EmailView
   INSTEAD OF UPDATE ON EmailView
BEGIN
   UPDATE EmailInfo SET MessageID = NEW.MessageID
      WHERE MessageID = OLD.MessageID;
   REPLACE INTO EmailView
      SELECT NEW.MessageID, NEW.ToAddr, NEW.FromAddr;
END;

Проблема

После того, как:

INSERT OR REPLACE INTO EmailView VALUES (1, 'alice@example.com', 'bob@example.com');
INSERT OR REPLACE INTO EmailView VALUES (2, 'alice@example.com', 'chad@example.com');

Обновленные строки содержат:

MessageID   ToAddr               FromAddr
---------   ------               --------
1           NULL                 bob@example.com
2           alice@example.com    chad@example.com

Есть NULL, которого там быть не должно. Соответствующая ячейка в таблице EmailInfo содержит потерянное значение ToAddrRef.

Если вы сделаете INSERT по одному, вы увидите, что идентификатор Алисы в таблице AddressLookup 1027 * изменится !

Похоже, это поведение задокументировано :

Предложение ON CONFLICT может быть указано как часть действия UPDATE или INSERT в теле триггера. Однако, если предложение ON CONFLICT указано как часть оператора, вызывающего срабатывание триггера, вместо этого используется политика обработки конфликта внешнего оператора.

Таким образом, «REPLACE» в операторе «INSERT OR REPLACE» верхнего уровня переопределяет критический «INSERT OR IGNORE» в программе запуска.

Есть ли способ заставить его работать так, как я хотел?

Ответы [ 2 ]

3 голосов
/ 01 мая 2010

Ну, так как SQLite является открытым исходным кодом, я просто изменю функцию codeTriggerProgram для обработки ON CONFLICT так, как должно быть.

1 голос
/ 27 марта 2012

У меня / была похожая проблема, и я думаю, что нашел способ обмануть sqlite, чтобы сделать это правильно, переместив OR REPLACE из внешнего оператора в саму таблицу:

CREATE TABLE a (id INTEGER PRIMARY KEY ON CONFLICT REPLACE, dataA TEXT);
CREATE TABLE b (id INTEGER PRIMARY KEY, dataB TEXT);

CREATE TRIGGER auto_add AFTER INSERT ON a
    BEGIN INSERT OR IGNORE INTO b (id) VALUES (NEW.id); END;

Когда вы вводите INSERT OR REPLACE в таблицу "a", вы всегда заменяете строку, добавленную в таблицу b, потому что триггер использует OR REPLACE сейчас.

Но если вы просто сделаете INSERT INTO, это, похоже, сработает, потому что обработка конфликта больше не является частью внешней вставки, а частью самой таблицы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...