sql - 更高效的SQLite触发器来“汇总”多行

标签 sql sqlite triggers

我正在从路由器收集传输数据;它提供每日,每月和每两分钟(间隔为120秒)的摘要。如果我在一天中(因此一个月中)重启路由器,则这些报告将不完整。但是,我仍然会得到间隔数据,并且可以对引导前后的记录进行汇总。

以前,我在更新三个单独的表之后使用脚本执行了此汇总操作。这很慢,因为我必须查询给定间隔的天数和月份间隔的总和。更新间隔后,仅执行汇总会更快。因此,我整理了一个触发器。问题是我编写触发器的方式会更新每个时间间隔插入的每日和每月行。理想情况下,每个事务仅处理一次并处理刚刚添加的行。

因此,下面是一个包含示例,显示了我所拥有的。 count列仅用于说明汇总发生的次数超出了需要。有没有一种方法可以简化这个过程?

BEGIN TRANSACTION;

CREATE TABLE monthly  (count INTEGER DEFAULT 0, date DATE NOT NULL, interface TEXT NOT NULL, upload INTEGER DEFAULT 0, download INTEGER DEFAULT 0, rollup_upload INTEGER DEFAULT 0, rollup_download INTEGER DEFAULT 0, PRIMARY KEY (date, interface));
CREATE TABLE daily    (count INTEGER DEFAULT 0, date DATE NOT NULL, interface TEXT NOT NULL, upload INTEGER DEFAULT 0, download INTEGER DEFAULT 0, rollup_upload INTEGER DEFAULT 0, rollup_download INTEGER DEFAULT 0, PRIMARY KEY (date, interface));
CREATE TABLE interval (count INTEGER DEFAULT 0, date DATE NOT NULL, interface TEXT NOT NULL, upload INTEGER DEFAULT 0, download INTEGER DEFAULT 0, interval INTEGER, PRIMARY KEY (date, interface));

CREATE TRIGGER rollup_interval_trigger AFTER INSERT ON interval
BEGIN

INSERT OR REPLACE INTO daily (count, date, interface, upload, download, rollup_upload, rollup_download)
SELECT
    COALESCE((SELECT count FROM daily WHERE date IS strftime('%Y-%m-%d', NEW.date, 'localtime') AND interface IS 'wan'),0)+1,
    strftime('%Y-%m-%d', NEW.date, 'localtime'),
    'wan',
    COALESCE((SELECT upload FROM daily WHERE date IS strftime('%Y-%m-%d', NEW.date, 'localtime') AND interface IS 'wan'),0),
    COALESCE((SELECT download FROM daily WHERE date IS strftime('%Y-%m-%d', NEW.date, 'localtime') AND interface IS 'wan'),0),
    sum(upload) as rollup_upload,
    sum(download) as rollup_download
FROM interval
WHERE strftime('%Y-%m-%d', date, 'localtime') = strftime('%Y-%m-%d', NEW.date, 'localtime') AND interface IS 'vlan2';

INSERT OR REPLACE INTO monthly (count, date, interface, upload, download, rollup_upload, rollup_download)
SELECT
    COALESCE((SELECT count FROM monthly WHERE date IS strftime('%Y-%m-01', NEW.date, 'localtime') AND interface IS 'wan'),0)+1,
    strftime('%Y-%m-01', NEW.date, 'localtime'),
    'wan',
    COALESCE((SELECT upload FROM monthly WHERE date IS strftime('%Y-%m-01', NEW.date, 'localtime') AND interface IS 'wan'),0),
    COALESCE((SELECT download FROM monthly WHERE date IS strftime('%Y-%m-01', NEW.date, 'localtime') AND interface IS 'wan'),0),
    sum(upload) as rollup_upload,
    sum(download) as rollup_download
FROM interval
WHERE strftime('%Y-%m', date, 'localtime') = strftime('%Y-%m', NEW.date, 'localtime') AND interface IS 'vlan2';

END;

COMMIT;

insert into daily (date, interface, download, upload) values ('2012-10-02', 'wan', 10, 20);
insert into monthly (date, interface, download, upload) values ('2012-10-01', 'wan', 30, 40);

.headers ON

select * from daily;
select * from monthly;

begin transaction;
insert into interval (date, interval, download, upload, interface) values ('2012-10-02 11:00:00', 120, 10, 20, 'vlan2');
insert into interval (date, interval, download, upload, interface) values ('2012-10-02 12:00:00', 120, 10, 20, 'vlan2');
insert into interval (date, interval, download, upload, interface) values ('2012-10-02 13:00:00', 120, 10, 20, 'vlan2');
insert into interval (date, interval, download, upload, interface) values ('2012-10-02 14:00:00', 120, 10, 20, 'vlan2');
insert into interval (date, interval, download, upload, interface) values ('2012-10-01 12:00:00', 120, 10, 20, 'vlan2');
insert into interval (date, interval, download, upload, interface) values ('2012-10-03 12:00:00', 120, 10, 20, 'vlan2');
commit;

select * from interval;

select * from daily;
select * from monthly;

最佳答案

仅通过插入间隔中的新数字来增加每日和每月的计数呢?这样一来,您就不会在每次插入时进行汇总。您所要做的就是通过主键对一行进行两次查找,并对该行进行更新。

CREATE TRIGGER rollup_interval_trigger AFTER INSERT ON interval
BEGIN
    INSERT INTO daily 
    SELECT 0, strftime('%Y-%m-%d', NEW.date, 'localtime'), 'wan',0,0,0,0 
    WHERE NOT EXISTS (
      SELECT 1 
      FROM daily 
      WHERE date = strftime('%Y-%m-%d', NEW.date, 'localtime') and interface = 'wan');

    UPDATE daily 
    SET count = count + 1,
        rollup_upload = rollup_upload + new.upload ,
        rollup_download = rollup_download + new.download 
    WHERE date = strftime('%Y-%m-%d', NEW.date, 'localtime') and interface = 'wan';

    -- and similarly for table monthly
END;


由于sqlite没有'upsert'语句,因此在更新之前,您需要另辟一条语句来创建每日/每月记录。

这将产生与触发测试数据相同的结果。

聚苯乙烯

如果需要,您还可以用INSERT或IGNORE替换INSERT ... WHERE NOT EXISTS以获得最大性能(我不太喜欢它,因为它捕获了所有冲突,而不仅是唯一的约束,而且通常可能隐藏其他冲突错误)。

关于sql - 更高效的SQLite触发器来“汇总”多行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20366368/

相关文章:

sql - Django 查询集 : Filter by reverse key and order by

sql - postgres string_agg 和 GROUP BY 子句

Sqlite 到 Postgres (Heroku) 日期/时间

ios - CoreData vs. SQLite vs. 自定义实体和 pLists

php - 解析错误 : syntax error, 意外的 T_IF

PHP:我的第一个登录系统不会验证密码

database - 应用程序密码和 SQLite 安全性

sql - 如何在插入后创建触发器而不在 postgres 中返回 NEW?

php - 基于触发器和存储过程更新汇总表

plsql - 插入更新触发器 PL/SQL 之后