2010-04-30 81 views
4

我有一个数据库(用于跟踪电子邮件统计)已经增长到数百兆字节,我一直在寻找方法来减少它。在触发器中插入或忽略

似乎大文件大小的主要原因是相同的字符串倾向于在成千上万行中重复。为了避免这个问题,我打算创建另一个表为一个字符串池,像这样:

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”。

有没有一种方法可以使它按照我想要的方式工作?

回答

3

那么,由于SQLite 开源,我只需更改codeTriggerProgram函数来处理它应该是的方式ON CONFLICT

1

我有/有类似的问题,我想我找到了一种方法来欺骗源码到这样做是正确的,从外部语句移动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它似乎工作,因为冲突处理不再是外部插入的一部分,而是表本身的一部分。