У меня есть база данных (для отслеживания статистики электронной почты), которая выросла до сотен мегабайт, и я искал способы уменьшить ее.
Кажется, что основная причина большого размера файла состоит в том, что одни и те же строки имеют тенденцию повторяться в тысячах строк. Чтобы избежать этой проблемы, я планирую создать еще одну таблицу для пула строк, например:
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» в программе запуска.
Есть ли способ заставить его работать так, как я хотел?