我有一个数据库(用于跟踪电子邮件统计)已经增长到数百兆字节,我一直在寻找方法来减少它。在触发器中插入或忽略
似乎大文件大小的主要原因是相同的字符串倾向于在成千上万行中重复。为了避免这个问题,我打算创建另一个表为一个字符串池,像这样:
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, '[email protected]', '[email protected]');
INSERT OR REPLACE INTO EmailView VALUES (2, '[email protected]', '[email protected]');
更新后的行包含:
MessageID ToAddr FromAddr
--------- ------ --------
1 NULL [email protected]
2 [email protected] [email protected]
有不应该有一个NULL。 EmailInfo
表中的相应单元格包含一个孤立的ToAddrRef
值。
如果您一次插入一个INSERT,您会看到AddressLookup
表中的Alice的ID更改为!
看来,这种行为是documented:
的ON CONFLICT子句可以被指定作为触发的主体内更新或插入动作的一部分。但是,如果将ON CONFLICT子句指定为引发触发器触发的语句的一部分,则将使用外部语句的冲突处理策略。
因此,顶层“INSERT OR REPLACE”语句中的“REPLACE”覆盖触发程序中的关键“INSERT OR IGNORE”。
有没有一种方法可以使它按照我想要的方式工作?