RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1285165
Accepted
MaxU - stop genocide of UA
MaxU - stop genocide of UA
Asked:2022-05-23 17:30:15 +0000 UTC2022-05-23 17:30:15 +0000 UTC 2022-05-23 17:30:15 +0000 UTC

用于存储差异和从增量恢复完整数据集的有效数据模型

  • 772

背景:我们有一个应用程序(用 Python 编写),它从各种来源接收数据,对其进行处理,并将结果存储在结果表中。结果表每次都会被覆盖,因为 结果是根据复杂算法组合来自不同来源的数据。有一个业务需求是能够比较不同日期的结果,因此我们现在将每天的结果存储在历史表中,并带有一个额外的字段tag来标识应用程序的每次运行。

例子:

数据:

 id   company_name   vat_id               url
123  Funny Company   123456 funny-company.com

数据历史:

       tag    id   company_name   vat_id               url
2021-05-23   123  Funny Company   123456 funny-company.com

每次运行的结果变化不大 - 大约 3-5% 的行被更改或添加,因此历史表中有 95% 是重复数据。

问题:请建议一个合适的数据模型,它只允许存储执行的最后一个结果,并且有“增量”表,可以让您轻松有效地
为应用程序的任何一天重新创建结果,并且不会占用太多空间?


UPD:在与受人尊敬的评论中交谈之后0xdb,asanisimov现在我倾向于选择以以下格式保存每天的增量:

在此处输入图像描述

在右侧,特定日期的恢复数据。现在我试图从左侧显示的数据中了解如何有效地实现这一点?

链接到 Excel 文件

sql
  • 4 4 个回答
  • 10 Views

4 个回答

  • Voted
  1. Roman-Stop RU aggression in UA
    2022-05-26T07:21:56Z2022-05-26T07:21:56Z

    我使用问题中描述的方式存储版本(尽管在 postgres 中)并进行了一些修改。

    有两个表data_head和data_history。通过继承,它们组合成data_all,我不知道oracle中是否有类似物,可能,您可以使用类似的东西,create view as select * from data_head union all select * from data_history好吧,或者union在请求中明确使用这个。具有特定类型的请求,即 使用视图、显式联合全部或 CTE 对代表性数据进行实验。

    这些表具有以下结构:

    id integer, snapshot_id integer, serial_number integer, modified_date timestamp, state text
    
    • id- 仅在 中唯一data_head,这是实体本身的标识符。
    • snapshot_id它是唯一的版本标识符。
    • serial_number- 版本号。随着每一次变化而增加。
    • modified_date- 版本的创建日期(类似于tag)。我每天有不止一次的变化。

    在某个时间点获取数据的请求如下所示:

    select p.*
    from data_all p
      left join data_all n on (n.id = p.id and n.serial_number = p.serial_number + 1)
    where p.modified_date <= :date
       and (:date < n.modified_date OR n.modified_date IS null)
       and p.state = 'active'
    

    当数据改变时,记录的副本data_head被插入到data_history中,并且data_head- 被更新。

    • 3
  2. Akina
    2022-05-27T17:22:19Z2022-05-27T17:22:19Z

    我不知道如何在 Oracle 中实现所有这些,但我必须在 MySQL 中做一个类似的系统。结果出现了以下情况。

    只有一张表用于存储数据——包括实际数据和历史数据。诚然,数据可变性更高,达到每月 40-50% 的水平,并且通常的每月分区足以确保足够的性能。

    该方案如下:

    CREATE TABLE data ( id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
                        { value_columns of different datatypes, }
                        valid_from DATE,
                        valid_till DATE DEFAULT '9999-12-31' )
    PARTITION BY RANGE (valid_till) (
        PARTITION p0 VALUES LESS THAN ('2000-01-01'),
        PARTITION p1 VALUES LESS THAN ('2000-02-01'),
     -- ...
        PARTITION pN VALUES LESS THAN ('2021-05-01'),
        PARTITION pcurrent VALUES LESS THAN MAXVALUE
    );
    

    插入记录时,触发器会检查是否存在与重要字段值相同的记录 和CURRENT_DATE < valid_till。

    在没有(新记录)的情况下,该记录被简单地插入。

    如果可用,则更新现有记录SET valid_till = CURRENT_DATE - INTERVAL 1 DAY,插入新记录。

    没错,我坚持使用两个请求而不是一个请求和一个触发器,但是同志们希望这样。

    然后执行第二个查询,找到表中所有不在更新数组中且匹配的记录CURRENT_DATE < valid_till,它们也运行SET valid_till = CURRENT_DATE - INTERVAL 1 DAY。

    因此,要获取某个日期的状态,我们只需获取该日期所在的所有记录BETWEEN valid_from и valid_till。分区允许您忽略 valid_till 小于给定日期的分区。

    为了进一步加快这样的查询,我建议将日期转换为天数,构建并使用空间索引——他们认为这不合适。

    PS。我不知道这个项目的命运。是的,它不再有趣了。

    • 3
  3. Best Answer
    0xdb
    2022-05-31T01:11:04Z2022-05-31T01:11:04Z

    我以问题中的数据模型为基础,几乎没有任何变化。data_hist不同的是,冗余数据不保存在历史表的更改中。也就是说,第一个插入总是只进入主表。

    为了在更改历史查询中考虑没有更改的新条目,它们的创建日期必须以某种方式存储。通常,该表已经有一个带有创建日期的列,例如:

    create table data (id, company_name, vat_id, url, created) as [...]
    

    给定日期的数据状态查询是对具有相关表连接(在本例中为两个表的连接)的最佳单个记录的标准搜索。它看起来像这样:

    with param (asofday) as (
        select date'2021-05-20' from dual 
    ), t (tag, id, company_name, vat_id, url, delete_flag) as (
        select date'9999-12-31', id, company_name, vat_id, url, null
        from data, param
        where created <= param.asofday 
        union all
        select tag, id, company_name, vat_id, url, delete_flag
        from data_hist)
    select lastchanged, id, company_name, vat_id, url, delete_flag
    from t t1
    cross apply (
        select max (t2.tag) keep (dense_rank last order by tag desc) lastchanged
        from t t2, param
        where t2.id = t1.id
        and t2.tag >= param.asofday
        group by t2.id
    ) where tag = lastchanged
    order by id;
    
    LASTCHANGED                 ID COMPA VAT_I URL     DELETE_FLAG
    ------------------- ---------- ----- ----- ------- -----------
    2021-05-20 00:00:00          1 aaa-3 123-3 aaa.com            
    2021-05-20 00:00:00          2 bbb   111   bbb.de             
    2021-05-21 00:00:00          3 ccc   345-2 ccc.de             
    2021-05-25 00:00:00          4 xxx   789   xxx.com           1
    

    插入“临时阶段”准备的新数据的简化示例:

    declare
        st stagetab;
    begin
        select stagerow (
            s.id, s.company_name, s.vat_id, s.url, s.created, 
            case when d.id is null then 0 else 1 end)  
        bulk collect into st
        from stage s
        left join data d on d.id = s.id;
        dbms_output.put_line ('staged '||sql%rowcount); 
    
        insert into data_hist
        select trunc (sysdate), id, company_name, vat_id, url, null  
        from data d
        where exists (
            select 1 
            from table (st) s
            where s.id = d.id and s.flagexists = 1);
        dbms_output.put_line ('copied '||sql%rowcount); 
        
        delete 
        from data d
        where exists (
            select 1 
            from table (st) s
            where s.id = d.id and s.flagexists = 1);
        dbms_output.put_line ('deleted '||sql%rowcount); 
    
        insert into data 
            select id, company_name, vat_id, url, created
            from table (st);
        dbms_output.put_line ('inserted '||sql%rowcount); 
    end;
    /
    

    用样本数据和创建添加。db<>fiddle上的对象。

    • 3
  4. 0xdb
    2022-06-07T08:42:53Z2022-06-07T08:42:53Z

    闪回解决方案。此解决方案不需要创建额外的表,它负责保存和删除更改历史记录。所有更改都将作为 UNDO 记录(或更改向量 oldval=>newval)存储在单独的表空间中,也就是说,以用于事务回滚和一致读取的相同格式存储。

    您将需要一个可供应用程序用户访问的附加表空间:

    create tablespace data_archive
        datafile '/path/dbfiles/oradata/cdb1/pdb1/data_archive01.dbf' 
        size 4M segment space management auto
    /
    alter user me quota unlimited on data_archive
    /
    

    接下来,创建一个存档并指示其中的数据存储时间(例如,6 个月),授予用户对新创建的存档的访问权限:

    create flashback archive data_hist
    tablespace data_archive quota 4M retention 6 month
    /
    grant flashback archive on data_hist to me;
    /
    

    表格可以选择将更改的数据保存在创建的存档中:

    create table me.data (id primary key, company_name, vat_id, url) as
        select rownum, 'company'||rownum, 'vat-'||rownum, 'domen'||rownum||'.com' 
        from dual connect by level<=9
    /
    alter table me.data flashback archive data_hist
    /
    

    从现在开始,表中所有更改的历史data可以使用 6 个月,并且可以通过带有AS OF子句的简单查询获得:

    update me.data set company_name=company_name||'*', vat_id=vat_id||'*'
    /
    9 rows updated.
    
    commit;
    
    Commit complete.
    
    select * 
    from me.data
    union all
    select * 
    from me.data as of timestamp (timestamp'2021-06-07 01:10:00')
    /
    
            ID COMPANY_NAME     VAT_ID   URL             
    ---------- ---------------- -------- ----------------
             1 company1*        vat-1*   domen1.com      
             2 company2*        vat-2*   domen2.com      
    [...]
             9 company9*        vat-9*   domen9.com      
             1 company1         vat-1    domen1.com      
             2 company2         vat-2    domen2.com       
    [...]    
    
    • 3

相关问题

  • 通过 OUT 参数从过程结果输出

  • ON 关键字附近的语法错误 - SQL

  • 多表查询中的 Count() 聚合函数

  • 根据时间更改单元格中的日期

  • phpMyAdmin 中的错误 #1064 SQL 查询

  • Qt:包含变量的数据库查询

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    表格填充不起作用

    • 2 个回答
  • Marko Smith

    提示 50/50,有两个,其中一个是正确的

    • 1 个回答
  • Marko Smith

    在 PyQt5 中停止进程

    • 1 个回答
  • Marko Smith

    我的脚本不工作

    • 1 个回答
  • Marko Smith

    在文本文件中写入和读取列表

    • 2 个回答
  • Marko Smith

    如何像屏幕截图中那样并排排列这些块?

    • 1 个回答
  • Marko Smith

    确定文本文件中每一行的字符数

    • 2 个回答
  • Marko Smith

    将接口对象传递给 JAVA 构造函数

    • 1 个回答
  • Marko Smith

    正确更新数据库中的数据

    • 1 个回答
  • Marko Smith

    Python解析不是css

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5