精华内容
下载资源
问答
  • 如何优化多数据关联报表

    千次阅读 2018-11-12 11:42:52
    数据关联报表是很常见的报表形式,它允许开发者分别从不同的来源(表或数据库)分别准备数据形成不同的数据集,在报表端(模板)通过表达式描述数据集间的关系完成关联。这样可以避免在数据准备时写过于复杂的 ...

    多数据集关联报表是很常见的报表形式,它允许开发者分别从不同的来源(表或数据库)分别准备数据形成不同的数据集,在报表端(模板)通过表达式描述数据集间的关系完成关联。这样可以避免在数据准备时写过于复杂的 SQL/ 存储过程,降低维护难度。尤其当报表数据来源于多个数据库时,多数据集的优势更加明显。

    凡事都有两面性,多数据集为开发带来方便的同时却对性能造成了极大的影响。在报表端进行多数据集关联时要计算关联表达式(举例:ds2.select(name,,id==A1))时,报表引擎一般会采用顺序遍历的方式进行,先拿一个数据集的第一条记录去第二个数据集中遍历查找符合条件的记录,然后是第二条,第三条…。因此两个数据集关联的时间复杂度是 O(n²),数据量不大时感受还不明显,数据量稍大一些就会很慢,随着数据集数量的增多报表性能也会呈指数下降。

    因此在实际报表业务中,当多数据集关联导致报表性能降低时可以考虑将多个数据集 SQL 合并成一句,利用数据库的关联计算能力提升性能。但这种方式又会导致 SQL 过于复杂,很难维护,而太复杂的 SQL 很可能被数据库搞错优化路径,结果性能仍不可控。并且合并 SQL 的方式有适用场景的限制(如无法完成跨异构库关联、文本关联等)。

    下面介绍采用集算器的优化方法,写法简单且性能高,能够普遍适用于各种场景:

    1. 单数据库,多个数据集 SQL 比较复杂,很难写成一句
    2. 单数据库,多数据集中使用了存储过程,无法整合成一句 SQL
    3. 单数据库,多数据集合并成一句 SQL 后性能仍不如人意
    4. 多数据库,多数据集来源多个数据库,无法通过一句 SQL 进行查询
    5. 涉及文件数据,多数据集中部分数据来自文件,无法使用 SQL 进行统一查询

    不同于 SQL(关系代数)采用笛卡尔积再过滤的方式看待 JOIN,基于离散数据集模型的集算器将关联运算做了区分(只考虑等值 JOIN):多对一的主外键表采用外键属性化方式关联、一对一的同维表采用同维表等同化方式关联、一对多的主子表采用主子表一体化关联,针对不同的表间关系采用不同算法进行运算,可以获得更简单的写法和更高的性能以及更广泛的适用范围。

    我们将通过一些示例来说明面向各种情况时,如何使用集算器获得最优的实现和效率。需要说明的是,为了描述方便我们使用抽象后最简单的情况说明各种关联运算,实际业务会复杂得多,每个数据集 SQL 也会复杂得多,但是不管怎样多数据集关联关系也逃不出多对一、一对一和一对多的情况,所以拿原子操作来说明问题,以期大家遇到问题时可以采用最合适的方式处理。

    报表集成

    这里假定读者已经了解集算器与报表的关系,集算器仅为报表提供数据准备,将原来的多数据集通过集算器完成关联计算,将计算以结果以单 / 多数据集的方式提供给报表进行呈现。

    集算器脚本可以直接被润乾报表 5.0 及以上版本直接引用(集算器数据集);如果是其他报表工具,集算器提供了标准 JDBC 和 ODBC 接口,可以采用类似调用存储过程的方式调用集算器脚本,详细可以参考教程《应用集成 - 被 JAVA 调用》章节,以及《集算器与 BIRT 集成》或《集算器与 JasperReport 集成》。

    因此下面大部分例子将省略报表制作部分,主要说明集算器处理多数据集关联计算的过程。

    外键表(多对一)

    表 A 的某些字段与表 B 的主键关联。A 表称为事实表,B 表称为维表。A 表中与 B 表主键关联的字段称为 A 指向 B 的外键,B 也称为 A 的外键表。外键表是多对一的关系,且只有 JOIN 和 LEFT JOIN,一般不会用到 FULL JOIN。如:订单表和客户表

    Orders 表和 Customer 表的主键都是其中的 id 字段,Orders 表的 customerID 字段是指向 Customer 表的外键。

    这里说的主键是指逻辑上的主键(下同),也就是在表中取值唯一的字段(组),一个表上可能有多个字段(组)都取值唯一(并不常见),可以认为都是主键。不是一定是在物理表上建立的那个主键。

    单外键举例

    报表中有两个数据集,数据分别来自订单信息表(Orders)和客户表(Customer)(实际业务中可能是两条复杂 SQL),订单表的客户 ID 指向客户表的主键客户 ID,属于典型的主外键关系。

    【计算目标】 查询某时间段内订单和客户详单

    集算器数据准备

    单库情况

    当两个数据集来源于单个数据库,数据集 SQL 比较复杂不易合并时,通过集算器实现多对一关联计算,脚本如下:

      A B
    1 =connect(“db”) / 建立数据库连接
    2 =A1.query(“select * from 订单 where 订购日期 >=? and 订购日期 <=?”,begin,end) / 查询订单数据
    3 =A1.query@x(“select * from 客户”) / 查询客户数据
    4 >A2.switch(客户 ID,A3: 客户 ID) / 关联,在 A2 订单表客户 ID 字段上建立指向客户表的指针
    5 =A2.new(客户 ID. 公司名称: 客户名称, 订单 ID, 订购日期, 运货费, 订单金额) / 通过外键属性化的方式,将外键表字段作为客户 ID 属性使用

    脚本解析:

    1、前 3 行连接数据库后分别取订单和客户数据作为两个独立数据集(事实上 A2 和 A3 的 SQL 可以任意复杂,取数阶段无需将两条 SQL 合并,分别查询即可);这里为了说明指针与记录,将两个表所有字段都选出,实际业务中应该用哪些字段取哪些。

    2、A2 中使用了脚本参数 begin 和 end 来接收起止时间范围

    3、注意 A3 的 query 函数使用了 @x 选项,代表查询后关闭连接,使用完数据库连接一定要及时关闭(也可以通过 Aclose() 显示关闭数据库连接)

    4、A4 中通过 switch 函数在 A2 订单表的客户 ID 字段上建立指向客户表记录的指针实现关联

    5、A5 利用建立关联关系通过“外键字段. 维表字段”的方式进行引用,如“客户 ID: 客户名称”,将维表记录看做外键的的属性,这便是外键属性化的由来;

    6、A5 为报表返回关联后结果集

    关于 switch 函数

    在 SQL 的概念体系中并不区分外键表和主子表,多对一和一对多从 SQL 的观点看来只是关联方向不同,本质上是一回事。比如,订单也可以理解成订单明细的外键表。但是,集算器把它们区分开,在简化语法和性能优化时使用不同的手段。

    switch 是集算器中实现多对一关联的函数,通过建立事实表和维表之间的外键指针实现连接。其原理是通过 HASH 算法在外键字段上建立指向维表记录的指针,这样在建立关联的时间与数据库中最快的关联方式 HASH JOIN 一样,但接下来使用连接结果时就不需要再查找 HASH TABLE,直接通过指针定位到内存中的维表记录。

    建立外键指针后外键字段的原值不再存储,而被转化为指向维表记录的指针,所有维表字段都可以通过“外键字段. 维表字段”方式引用,因此 switch 函数只适合做单外键的关联(原外键字段值变了),多外键关联时需要使用 A.join 函数(后面会说明多外键情况)。

    指针式连接的意义在于一次建立多次使用,重复使用时由于无需再建立连接性能高效得多。如上述例子中,除了获取订单和客户详单,还想针对客户所在区域汇总订单数量,那么可以写成这样(B5 格):

      A B
    1 =connect(“db”)  
    2 =A1.query(“select * from 订单 where 订购日期 >=begin and 订购日期 <=end”)  
    3 =A1.query@x(“select * from 客户”)  
    4 >A2.switch(客户 ID,A3: 客户 ID)  
    5 =A2.new(客户 ID. 公司名称: 客户名称, 订单 ID, 订购日期, 运货费, 订单金额) =A2.groups(客户 ID. 所在区域;count(订单 ID):num)

    B5 的计算继续使用了在 A2 客户 ID 字段上建立的指针,而无需重新建立关联。实际应用中,指针式关联建立后,重复使用次数越多性能优势越明显。

    在报表中复用连接,计算不同的结果集多用于分片报表,分片报表在报表业务中并非很常见,但也不算罕见,不过对应业务都比较复杂,不大合适举例,这里就不细说了。当遇到报表分片且有相同关联情况时可以考虑使用集算器进行连接复用。

    多库情况

    前面提到多库尤其是异构多库情况下无法利用 SQL 做关联计算,在报表中计算性能又低,这时非常适合使用集算器来做。下面假设订单和客户表分别来源两个不同数据库 db1 和 db2,计算目标仍然是:查询某时间段内订单和客户详单,来看集算器的具体写法。

      A B
    1 =connect(“db1”) =connect(“db2”)
    2 =A1.query@x(“select * from 订单 where 订购日期 >=? and 订购日期 <=?”,begin,end)  
    3 =B1.query@x(“select * from 客户”)  
    4 >A2.switch(客户 ID,A3: 客户 ID)  
    5 =A2.new(客户 ID. 公司名称: 客户名称, 订单 ID, 订购日期, 运货费, 订单金额)  

    注意到和单库情况的区别了吗?

    多库情况只需要在脚本中建立多库的连接(A1 和 B1)分别执行 SQL 查询(A2 和 A3),剩下的运算和单库完全一致,轻松实现基于多库的关联计算。

    事实上,集算器(脚本)还非常利于应用移植和数据库扩展,当底层数据库发生变化或者由单库拆分成多库时,只需更改数据库连接,主要的计算逻辑完全不用改。更进一步,如果连接信息也维护在配置中,则可以写出更加通用的脚本做到系统扩展时脚本无缝移植。

    涉及文本

    集算器作为开放计算引擎提供了多数据源支持,除了关系数据库外,本地文件(Excel、TXT、CSV、JSON/XML)、NoSQL、Hadoop 等也可以直接作为数据源参与运算。因此如果报表中有数据来源于文本、Excel 等文件,可以通过集算器直接处理(SQL 就无能为力了)。

    沿用上面的例子,假设客户信息来源于 TXT,计算目标仍然是:查询某时间段内订单和客户详单。来看集算器的写法。

      A B
    1 =connect(“db1”)  
    2 =A1.query@x(“select * from 订单 where 订购日期 >=? and 订购日期 <=?”,begin,end)  
    3 =file(“/usr/ 客户.txt”).import@t() / 读入文件数据
    4 >A2.switch(客户 ID,A3: 客户 ID)  
    5 =A2.new(客户 ID. 公司名称, 订单 ID, 订购日期, 运货费, 订单金额)  

    涉及到文本有什么变化吗?只将 A3 改为读取文件数据即可,核心计算逻辑仍然没有变化。

    上面我们通过多对一的两个表对单库、多库和文件三种情况进行说明,报表遇到相应问题可以使用集算器处理。实际业务中还可能涉及多层外键情况,即多表外键关联。

    多层外键关联举例

    报表中有三个数据集,数据分别来自订单信息表(Orders)、客户表(Customer)和地区表(Area),订单表的客户 ID 指向客户表的主键客户 ID,客户表的所在区域指向区域表的主键区域 ID。

    【计算目标】 查询某时间段内订单及其客户与所在区域详细信息

    集算器数据准备

      A B
    1 =connect(“db”)  
    2 =A1.query(“select * from 订单 where 订购日期 >=? and 订购日期 <=?”,begin,end)  
    3 =A1.query(“select 客户 ID, 公司名称 from 客户”) =A1.query@x(“select 区域 ID, 区域名称 from 地区”)
    4 >A3.switch(所在区域,B3: 区域 ID) >A2.switch(客户 ID,A3: 客户 ID)
    5 =A2.new(客户 ID. 所在区域. 区域名称: 区域, 客户 ID. 公司名称: 客户, 订单 ID, 订购日期, 运货费, 订单金额)  

    脚本解析:

    1、A2-B4 分别查询订单、客户和地区数据

    2、A4 中通过 switch 函数在 A3 所在区域上建立指向地区记录的指针实现关联

    3、同理,B4 在 A2 订单表的客户 ID 字段上建立指向客户表记录的指针实现关联,这里得到了一个三层结果的集合

    4、A5 通过外键属性化的方式引用区域和客户信息,可以看到无论有多少层外键都可以通 过 [点](.)的方式作为外键属性引用

    在实际业务中很常见的星型结构还会涉及到同一个事实表和多个维表进行关联,不同于传统的 HASH 分段 JOIN 方案,集算器无需两两消除、多次遍历,通过遍历一次事实表即可完成与多个维表的关联,非常高效,适合多数据库表关联性能低下需要改善的场景。

    下面以一个事实表与两个维表关联说明多维表情况下集算器处理方式。

    关联多个维表举例

    订单信息表(Orders)与客户表(Customer)、雇员表(Employee),订单表的客户 ID 指向客户表的主键客户 ID;销售 ID 指向雇员表的员工 ID

    【计算目标】 按客户所在区域和销售人员汇总订单金额

    集算器数据准备

    集算器实现脚本:

      A B
    1 =connect(“db”)  
    2 =A1.query(“select * from 订单”)  
    3 =A1.query(“select * from 客户”)  
    4 =A1.query@x(“select * from 雇员”)  
    5 >A2.switch(客户 ID,A3: 客户 ID; 雇员 ID,A4: 雇员 ID) / 同时关联两个维表
    6 =A2.groups(客户 ID. 地区: 地区, 雇员 ID. 姓名: 姓名;sum( 订单金额):amount) / 外键属性化方式访问维表字段,汇总指标

    在 A5 中通过 switch 将订单信息同时与客户表和雇员表关联,客户 ID 和雇员 ID 分别指向对应维表的记录

    这里可以看到,通过遍历一次订单表就关联了客户和雇员,当外键关联较多时使用 switch 更加简单高效。相反,在写 SQL 关联多个表时,偶尔会出现漏写 join 条件导致数据库被跑死的情况,而集算器则完全避免了这种情况。

    多外键情况举例

    单外键下无论是数据来源数据库或是文件均可使用 switch 进行处理,实际业务中还可能存在多外键的情况。报表中有两个数据集分别来自学生表(Students)和班级表(Classes),学生表的专业号和班级号为外键字段,分别指向班级表的联合主键(专业号,班级号)。

    【计算目标】 查询所有学生的学号,姓名,专业,班级,班主任

    集算器数据准备

      A B
    1 =connect(“db”)  
    2 =A1.query(“select * from 学生”)  
    3 =A1.query@x(“select * from 班级”).keys(专业号, 班级号)  
    4 =A2.join(专业号: 班级号,A3, 班主任) / 双主键关联

    脚本解析:

    1、A3 查询班级数据,并通过 keys 设置班级的主键为专业号和班级号;

    2、A4 使用 A.join() 函数进行双主键关联,将班主任信息添加到学生信息中,形成目标结果集

    与 switch 处理单外键关联不同,当出现多外键的情况下需要使用 A.join 完成关联。

    同维表(一对一)

    表 A 的主键与表 B 的主键关联,A 和 B 互称为同维表。同维表是一对一的关系,JOIN、LEFT JOIN 和 FULL JOIN 的情况都会有,如:员工表和经理表。

    两个表的主键都是 id,经理也是员工,两表共用同样的员工编号,经理会比普通员多一些属性,另用一个经理表来保存。

    单主键举例

    报表中有三个数据集,分别来自回款表(OrderPayment)、客户表(Customer)和订单表(Orders),回款表的客户 ID 指向客户表主键客户 ID,订单表的客户 ID 指向客户表主键客户 ID。

    【计算目标】 按客户(所有)查看某时间段订单总额和回款总额

    这是很常见的一类报表,按照某个维度(如地区、日期、人员)汇总多个指标(如订单额、回款额),但我们发现报表的三个数据集之间并不是像销售表和员工表(主键都是人员 ID)那样互为同维表,不过结合计算目标分析一下,由于一个客户会有多笔订单和回款记录,因此需要对两个表分别按照客户 ID 分组后(结果集以客户 ID 为主键)向客户表主键客户 ID 对齐,显然三个集合是一组以客户 ID 为主键的同维表。

    集算器数据准备

      A B
    1 =connect(“db”)  
    2 =A1.query(“select 客户 ID,sum( 订单金额) 订单金额 from 订单 where 订购日期 >=? and 订购日期 <? group by 客户 ID”)  
    3 =A1.query(“select 客户 ID,sum( 回款金额) 回款金额 from 回款 where 回款日期 >=begin and 回款日期 <=end group by 客户 ID “)  
    4 =A1.query@x(“select 客户 ID, 公司名称 from 客户”)  
    5 =join@1(A4: 客户, 客户 ID;A2: 订单, 客户 ID;A3: 回款, 客户 ID)  
    6 =A5.new(客户. 公司名称: 客户名称, 订单. 订单金额: 订单金额, 回款. 回款金额: 回款金额)  

    脚本解析:

    1、A2 和 A3 针对订单和回款数据分别按照客户 ID 进行分组汇总;

    2、A5 按照客户表左关联(@1 选项代表左连接)订单和回款数据

    3、A6 获得关联结果返回报表数据集

    这里关注一下 join 函数(上述例子 A5=join@1(A4: 客户, 客户 ID; A2: 订单, 客户 ID;A3: 回款, 客户 ID)),可以看到 join 的各个表之间看起来似乎是无关的,在集算器中关联时无需关注表间关系,只需要同时向某一个维度(如客户维度)对齐即可,这样在关联表增多或减少时修改非常方便。如果是 SQL 的写法必须指定两个表的关联条件,关联的表数量太多时就容易漏写一两个条件导致出现叉乘算错的情况,如果漏写条件的表比较大,还容易把数据库跑死;集算器的 join 则避免了这种情况。

    另外,从上述例子来看当涉及多个事实表同时向维表对齐汇总时,一定要先 group 再 join,如果先 join 再 group 就会算错,写成 SQL 应该是维表和有两个 group by 的子查询 join。

    多主键情况举例

    与多外键情况类似,当同维表采用联合主键时就会存在多主键同维表关联的情况。报表中有两个数据集,分别来自回款表(OrderPayment)和订单表(Orders),两个表没有关联关系。

    【计算目标】 按客户和年份汇总回款金额和订单金额

    这两个表直接并没有关联关系,但经过同样两个维度分组汇总后,就形成了两个以客户和日期为主键的同维表

    按照计算目标,要同时获得回款金额和订单金额,需要将两个表进行关联计算。

    集算器数据准备

      A B
    1 =connect(“db”)  
    2 =A1.query(“select 客户 ID,year( 订购日期) 年份,sum(订单金额) 订单金额 from 订单 where 订购日期 >=? and 订购日期 <=? group by 客户 ID, 年份”,begin,end)  
    3 =A1.query(“select 客户 ID,year( 回款日期) 年份,sum(回款金额) 回款金额 from 回款 where 回款日期 >=? and 回款日期 <=? group by 客户 ID, 年份 “,begin,end)  
    4 =join@f(B2: 订单, 客户 ID, 年份;B3: 回款, 客户 ID, 年份)  
    5 =A4.new(订单. 客户 ID: 客户, 订单. 年份: 年份, 订单. 订单金额: 订单金额, 回款. 回款金额: 回款金额)  

    脚本解析:

    1、A2 和 A3 分别查询订单和回款数据,并按客户和年份汇总订单额和回款额;

    2、A4 通过全连接对齐带有两个主键(客户 ID, 年份)的结果集

    3、A5 根据关联结果返回报表数据集

    同维表与外键表混合

    在实际业务中还经常能见到同维表和外键表混合使用的情况,集算器处理起来仍然简单高效。

    举例

    沿用上述单主键同维表的例子,现在还有一张地区表(Area),客户表外键字段所在区域指向区域表主键区域 ID。

    【计算目标】 按客户所在区域和客户查看某时间段订单总额和回款总额

    分析后仍然得到下面的同维表,只不过客户表外键字段所在区域又指向了地区表,出现了同维表和外键表混合的情况。

    集算器数据准备

      A B
    1 =connect(“db”)  
    2 =A1.query(“select 客户 ID,sum( 订单金额) 订单金额 from 订单 where 订购日期 >=? and 订购日期 <=? group by 客户 ID”,begin,end)  
    3 =A1.query(“select 客户 ID,sum( 回款金额) 回款金额 from 回款 where 回款日期 >=?  and 回款日期 <=? group by 客户 ID “,begin,end)  
    4 =A1.query(“select 客户 ID, 公司名称, 所在区域 from 客户”)  
    5 =A1.query@x(“select 区域 ID, 区域名称 from 区域”)  
    6 >A4.switch(所在区域,A5: 区域 ID)  
    7 =join@1(A4: 客户, 客户 ID;A2: 订单, 客户 ID;A3: 回款, 客户 ID)  
    8 =A7.new(客户. 所在区域. 区域名称: 区域, 客户. 公司名称: 客户名称, 订单. 订单金额: 订单金额, 回款. 回款金额: 回款金额)  

    脚本解析:

    1、A6 在 A4 客户信息中建立外键关联

    2、A7 关联后结果可以看到集算器的结果集可以是任意多层结构

    3、A8 通过外键属性化方式引用区域名称,为报表返回结果集

    主子表(一对多)

    表 A 的主键与表 B 的部分主键关联,A 称为主表,B 称为子表。主子表是一对多的关系,只有 JOIN 和 LEFT JOIN,不会有 FULL JOIN,如:订单和订单明细。

    Orders 表的主键是 id,OrderDetail 表中的主键是 (id,no),前者的主键是后者的一部分,订单表是主表,订单明细表子表。从子表去看主表,与前述提到的外键表非常类似,只是外键表不要求外键字段是主键,因此从子表角度观察表间关系可以将主子表看做外键表的特殊情况,所以有时也可以采用外键表处理关联的方法(switch)。

    主子表关联计算在报表中并不常见,即使有,多数情况下可以转换成将主表作为外键表关联,或者子表 group 后变成同维表处理。当主表作为外键表处理时,除了可以用到外键表(多对一)switch 的处理方式,还可以通过 join 实现。除了 join 可以关联多外键情况,当关联的两个结果集按照关联字段有序时还可以使用归并算法,性能比 switch 更高(数据量不大时优势并不明显)。

    子表关联主表将主表作为外键表可参考前述外键表中 < 单外键举例 >,子表 group 后变成同维表关联的例子可参考前述同维表中 < 单主键举例 >。

    下面介绍一种有序归并实施关联计算的方法,读者在对主子表(包括同维表)进行关联计算时也可选用,以获得更高性能。

    主子表有序归并举例

    报表有两个数据集,分别来自订单表(Orders)和订单明细表(OrderDetails)

    订单表主键与订单明细表部分主键关联,订单表是主表,订单明细表是子表。现在两个表都按照订单 ID 有序

    【计算目标】 查询某时间段内客户订单明细

    集算器数据准备

      A B
    1 =connect(“db”)  
    2 =A1.query(“select 订单 ID, 客户 ID from 订单 order by 订单 ID where 订购日期 >=? and 订购日期 <=?”,begin,end)  
    3 =A1.query@x(“select 订单 ID, 订购产品, 价格, 数量 from 订单明细 order by 订单 ID “)  
    4 =join@m(A2:o, 订单 ID;A3:od, 订单 ID)  
    5 =A4.new(o. 客户 ID: 客户,o. 订单 ID: 订单,od. 订购产品: 产品,od. 价格: 价格,od. 数量: 数量)  

    脚本解析:

    1、A2-A3 分别查询订单和订单明细数据,结果集按订单 ID 有序

    2、A4 通过有序归并算法(@m 选项)对两个集合按照订单 ID 关联

    3、A5 获得关联结果为报表返回结果集

    有序归并可以极大提高关联效率,下面简单解释一下。

    设两个关联表的规模(记录数)分别是 N 和 M,则 HASH 分段技术的计算复杂度(关联字段的比较次数)大概是 SUM(Ni*Mi),其中 Ni 和 Mi 分别是 HASH 值为 i 的两表记录数,满足 N=SUM(Ni) 和 M=SUM(Mi),这大概率会比完全遍历时的复杂度 N*M 要小很多(运气较好的时候会小 K 倍,K 是 HASH 值的取值范围)。

    如果这两个表针对关联键都有序,那么我们就可以使用归并算法来处理关联,这时的复杂度是 N+M;在 N 和 M 都较大的时候(一般都会远大于 K),这个数会远小于 SUM(Ni*Mi),这就是有序归并的好处。

    润乾报表层次数据集

    通过这些例子,集算器为报表准备数据时最终返回的均为标准 ResutSet,这就经常需要将集算器的分层结构(如 switch 和 join 后结果集)转换成标准的二维表,虽然转换工作不复杂,但如果能直接使用分层结果集会更加简单高效。

    润乾报表 5.0 及以上版本就支持直接使用带有层次的数据集进行数据呈现。沿用订单和订单明细的主子表结构。

    【报表展现目标】

    报表每个单元上面是订单信息(单条),下面是明细信息(多条),属于典型的主子报表。

    集算器数据准备

      A B
    1 =connect(“demo”)  
    2 =A1.query(“select 订单 ID, 客户 ID from 订单 order by 订单 ID where 订购日期 >=? and 订购日期 <=?”,begin,end)  
    3 =A1.query@x(“select 订单 ID, 订购产品, 价格, 数量 from 订单明细”) =A3.group(订单 ID)
    4 =join@1(A2:o, 订单 ID;B3:od, 订单 ID)  
    5 =A4.new(o. 客户 ID: 客户,o. 订单 ID: 订单,od)  

    脚本解析:

    1、B3 将订单明细按照订单 ID 分组,得到分组子集(保留分组成员)

    2、A4 订单关联订单明细,一条订单信息对应多条明细

    3、A5 生成报表可以接收的多层结果并为报表返回数据集

    润乾报表设计

    设置参数

    打开报表设计器,新建报表后设置查询参数

    设置数据集

    设置报表数据集,选择集算器数据集类型,添加上述准备好的集算器脚本文件,并设置报表参数与集算器脚本参数对应

    数据集设置后,在报表设计器右下角的数据集窗口中即输出层次结构如下:

    编写报表表达式

    直接使用集算器提供的层次结果集设置报表表达式,其中设置 B4、B5、B6、B7、B8 左主格为 C4(按照订单扩展)

    通过以上步骤即可完成基于层次数据集的报表设计,目前只有润乾报表提供了层次数据集支持,在制作主子表、分组明细报表时就可以在数据准备(数据集)阶段将数据准备好,然后为报表返回带有层次的数据集,报表直接引用无需再次关联或分组,可以带来更高的报表性能。

    子表有序计算举例

    区分主子表后,如果从主表观察子表常常会涉及分组子集和有序运算,这时用集算器处理就非常方便了。举一个并不十分常见的例子,读者可以感受一下。

    报表有三个数据集,分别来自回款表(OrderPayment)、订单表(Orders)和订单明细表(OrderDetails)。

    订单表的主键是订单 ID,回款表的主键是(编号,订单 ID),订单明细表的主键是(编号,订单 ID),订单的主键是回款和订单明细的一部分,订单表是主表,回款表和订单明细表是子表。

    【计算目标】 统计每个客户的每个订单中,最大和最小两笔回款,最高和最低两个价格

    这里并不是计算汇总值,而是要找出每个客户的每笔订单中回款金额最大和最小的两笔回款,以及每笔订单中订购产品最高和最低的两个价格,用以识别客户类型及其回款能力。

    集算器数据准备

      A B C
    1 =connect(“db”)    
    2 =A1.query(“select 订单 ID, 客户 ID from 订单 order by 客户 ID, 订单 ID”)    
    3 =A1.query(“select 订单 ID, 回款金额 from 回款 order by 订单 ID, 回款金额 desc”) =A3.group(订单 ID) =B3.(.m(1).m(-1))
    4 =A1.query@x(“select 订单 ID, 单价 as 价格 from 订单明细 order by 订单 ID, 价格 desc”) =A4.group(订单 ID) =B4.(.m(1).m(-1))
    5 =join@1(A2:o, 订单 ID;C3:op, 订单 ID;C4:od, 订单 ID)    
    6 =A5.new(o. 客户 ID: 客户,o. 订单 ID: 订单,op.m(1). 回款金额: 最大回款金额,op.m(-1). 回款金额: 最小回款金额,od.m(1). 价格: 最高单价,od.m(-1). 价格: 最低单价 )    

    脚本解析:

    1、A2 查询订单数据,按照客户和订单排序

    2、A3 查询回款数据,按照订单排序,回款金额降序

    3、B3 按照订单 ID 分组,由于要查找分组成员(最大和最小值),所以这里需要使用 group 函数分组并保留分组结果(不聚合)

    4、C3 找出每组中回款金额最大和最小两条记录

    5、同理 A4-C4 按照订单分组查找每组中价格最高和最低两条记录

    6、A5 根据客户和订单信息左关联上述两个结果集(注意:关联一定要在前面两个分组后进行,如果先关联则会出现多对多叉乘,导致结果错误)。

    每一条关联结果,订单只有一条记录,回款和订单明细则包含两条记录,这是主子表关联关联计算的特点,主表的一条记录指向子表的多条记录

    7、根据关联结果,生成最终结果集,并为报表返回结果集

    以上通过集算器关联运算解决了多数据集关联报表的性能问题,实测中报表性能可获得数倍到数百倍的提升(随数据规模和关联表数量线性增长)。同时集算器解决方案实现比较简单,适用范围更广,适用于数据库无法完成的跨异构库关联、文本关联等情况,从而为报表性能优化、降低报表应用耦合性提供了新思路。

    展开全文
  • litepal数据关联

    千次阅读 2018-04-27 18:03:23
    最近用litepal操作数据很方便,记录下表关联 1. 一对一 song和introduction 即音乐和简介 Song中持有introduction 的引用,introduction 持有Song的引用,就建立了一对一关系, - 增 如下面代码所示 - 删 删除...

    最近用litepal操作数据很方便,记录下表关联
    1. 一对一
    song和introduction 即音乐和简介 Song中持有introduction 的引用,introduction 持有Song的引用,就建立了一对一关系,
    - 增 如下面代码所示
    - 删 删除song一行,对应的introduction 自动删除,反之亦然。
    - 改 修改introduction 某行,对应的song自动被修改

    Introduction introduction = new Introduction();
    introduction.setTitle("这是song1111 介绍");
    Song song1 = new Song();
    song1.setTitle("song111");
    song1.setIntroduction(introduction);
    introduction.save();
    song1.save();
    Song songData = DataSupport.findLast(Song.class, true);
    Introduction introductionData = DataSupport.findLast(Introduction.class, true);
    Log.e(TAG, "onCreate:songData " + songData);
    Log.e(TAG, "onCreate:introductionData " + introductionData);
    1. 多对一
      歌曲和评论 多对一

    歌曲持有评论的集合即可
    - 增 见代码
    - 删 删多的一方,一的一方自动修改
    - 改 同上

        Song song1 = new Song();
            song1.setTitle("song111");
            Commment comment1 = new Commment();
            comment1.setTitle("111");
            Commment comment2 = new Commment();
            comment2.setTitle("222");
            song1.getCommentList().add(comment1);
            song1.getCommentList().add(comment2);
            comment1.save();
            song1.save();
    1. 多对多
    Song song1 = new Song();
            song1.setTitle("song111");
            song1.save();
    
    
            Song song2 = new Song();
            song2.setTitle("song222");
            song2.save();
    
    
            Category category1 = new Category();
            category1.setTitle("111");
            category1.getSongArrayList().add(song1);
            category1.getSongArrayList().add(song2);
            category1.save();
    
            Category category2 = new Category();
            category2.setTitle("222");
            category2.getSongArrayList().add(song1);
            category2.getSongArrayList().add(song2);
            category2.save();
    展开全文
  • 数据挖掘十大算法(四):Apriori(关联分析算法)

    万次阅读 多人点赞 2018-08-29 21:38:50
    同样的该算法也是在一堆数据集中寻找数据之间的某种关联,这里主要介绍的是叫做Apriori的‘一个先验’算法,通过该算法我们可以对数据集做关联分析——在大规模的数据中寻找有趣关系的任务,本文主要介绍使用Apriori...

    终于到了机器学习实战的第十一章了,这也是继K-均值后的第二个无监督学习算法了。同样的该算法也是在一堆数据集中寻找数据之间的某种关联,这里主要介绍的是叫做Apriori‘一个先验’算法,通过该算法我们可以对数据集做关联分析——在大规模的数据中寻找有趣关系的任务,本文主要介绍使用Apriori算法发现数据的(频繁项集、关联规则)。

    这些关系可以有两种形式:频繁项集、关联规则

            频繁项集:经常出现在一块的物品的集合

            关联规则:暗示两种物品之间可能存在很强的关系

    一个具体的例子:

    频繁项集是指那些经常出现在一起的物品,例如上图的{葡萄酒、尿布、豆奶},从上面的数据集中也可以找到尿布->葡萄酒的关联规则,这意味着有人买了尿布,那很有可能他也会购买葡萄酒。那如何定义和表示频繁项集和关联规则呢?这里引入支持度和可信度(置信度)。

    支持度:一个项集的支持度被定义为数据集中包含该项集的记录所占的比例,上图中,豆奶的支持度为4/5,(豆奶、尿布)为3/5。支持度是针对项集来说的,因此可以定义一个最小支持度,只保留最小支持度的项集。

    可信度(置信度):针对如{尿布}->{葡萄酒}这样的关联规则来定义的。计算为 支持度{尿布,葡萄酒}/支持度{尿布},其中{尿布,葡萄酒}的支持度为3/5,{尿布}的支持度为4/5,所以“尿布->葡萄酒”的可行度为3/4=0.75,这意味着尿布的记录中,我们的规则有75%都适用。

    有了可以量化的计算方式,我们却还不能立刻运算,这是因为如果我们直接运算所有的数据,运算量极其的大,很难实现,这里说明一下,假设我们只有 4 种商品:商品0,商品1,商品 2,商品3. 那么如何得可能被一起购买的商品的组合?

    上图显示了物品之间所有可能的组合,从上往下一个集合是 Ø,表示不包含任何物品的空集,物品集合之间的连线表明两个或者更多集合可以组合形成一个更大的集合。我们的目标是找到经常在一起购买的物品集合。这里使用集合的支持度来度量其出现的频率。一个集合出现的支持度是指有多少比例的交易记录包含该集合。例如,对于上图,要计算 0,3 的支持度,直接的想法是遍历每条记录,统计包含有 0 和 3 的记录的数量,使用该数量除以总记录数,就可以得到支持度。而这只是针对单个集合 0,3. 要获得每种可能集合的支持度就需要多次重复上述过程。对于上图,虽然仅有4中物品,也需要遍历数据15次。随着物品数目的增加,遍历次数会急剧增加,对于包含 N 种物品的数据集共有 2^N−1 种项集组合。为了降低计算时间,研究人员发现了 Apriori 原理,可以帮我们减少感兴趣的频繁项集的数目。

    Apriori 的原理:如果某个项集是频繁项集,那么它所有的子集也是频繁的。即如果 {0,1} 是频繁的,那么 {0}, {1} 也一定是频繁的。

    这个原理直观上没有什么用,但是反过来看就有用了,也就是说如果一个项集是非频繁的,那么它的所有超集也是非频繁的。如下图所示:

    频繁项集:

    主要步骤:

        首先会生成所有单个物品的项集列表
        扫描交易记录来查看哪些项集满足最小支持度要求,那些不满足最小支持度的集合会被去掉
        对剩下的集合进行组合以生成包含两个元素的项集
        接下来重新扫描交易记录,去掉不满足最小支持度的项集,重复进行直到所有项集都被去掉

    代码(代码中有详细注释):

    from numpy import *
    
    # 构造数据
    def loadDataSet():
        return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
    
    # 将所有元素转换为frozenset型字典,存放到列表中
    def createC1(dataSet):
        C1 = []
        for transaction in dataSet:
            for item in transaction:
                if not [item] in C1:
                    C1.append([item])
        C1.sort()
        # 使用frozenset是为了后面可以将这些值作为字典的键
        return list(map(frozenset, C1))  # frozenset一种不可变的集合,set可变集合
    
    # 过滤掉不符合支持度的集合
    # 返回 频繁项集列表retList 所有元素的支持度字典
    def scanD(D, Ck, minSupport):
        ssCnt = {}
        for tid in D:
            for can in Ck:
                if can.issubset(tid):   # 判断can是否是tid的《子集》 (这里使用子集的方式来判断两者的关系)
                    if can not in ssCnt:    # 统计该值在整个记录中满足子集的次数(以字典的形式记录,frozenset为键)
                        ssCnt[can] = 1
                    else:
                        ssCnt[can] += 1
        numItems = float(len(D))
        retList = []        # 重新记录满足条件的数据值(即支持度大于阈值的数据)
        supportData = {}    # 每个数据值的支持度
        for key in ssCnt:
            support = ssCnt[key] / numItems
            if support >= minSupport:
                retList.insert(0, key)
            supportData[key] = support
        return retList, supportData # 排除不符合支持度元素后的元素 每个元素支持度
    
    # 生成所有可以组合的集合
    # 频繁项集列表Lk 项集元素个数k  [frozenset({2, 3}), frozenset({3, 5})] -> [frozenset({2, 3, 5})]
    def aprioriGen(Lk, k):
        retList = []
        lenLk = len(Lk)
        for i in range(lenLk): # 两层循环比较Lk中的每个元素与其它元素
            for j in range(i+1, lenLk):
                L1 = list(Lk[i])[:k-2]  # 将集合转为list后取值
                L2 = list(Lk[j])[:k-2]
                L1.sort(); L2.sort()        # 这里说明一下:该函数每次比较两个list的前k-2个元素,如果相同则求并集得到k个元素的集合
                if L1==L2:
                    retList.append(Lk[i] | Lk[j]) # 求并集
        return retList  # 返回频繁项集列表Ck
    
    # 封装所有步骤的函数
    # 返回 所有满足大于阈值的组合 集合支持度列表
    def apriori(dataSet, minSupport = 0.5):
        D = list(map(set, dataSet)) # 转换列表记录为字典  [{1, 3, 4}, {2, 3, 5}, {1, 2, 3, 5}, {2, 5}]
        C1 = createC1(dataSet)      # 将每个元素转会为frozenset字典    [frozenset({1}), frozenset({2}), frozenset({3}), frozenset({4}), frozenset({5})]
        L1, supportData = scanD(D, C1, minSupport)  # 过滤数据
        L = [L1]
        k = 2
        while (len(L[k-2]) > 0):    # 若仍有满足支持度的集合则继续做关联分析
            Ck = aprioriGen(L[k-2], k)  # Ck候选频繁项集
            Lk, supK = scanD(D, Ck, minSupport) # Lk频繁项集
            supportData.update(supK)    # 更新字典(把新出现的集合:支持度加入到supportData中)
            L.append(Lk)
            k += 1  # 每次新组合的元素都只增加了一个,所以k也+1(k表示元素个数)
        return L, supportData
    
    dataSet = loadDataSet()
    L,suppData = apriori(dataSet)
    print(L)
    print(suppData)

    返回频繁项集与支持度

    上面代码获取数据的频繁项集,下面通过其他函数来获得关联规则。

    关联规则:

    # 获取关联规则的封装函数
    def generateRules(L, supportData, minConf=0.7):  # supportData 是一个字典
        bigRuleList = []
        for i in range(1, len(L)):  # 从为2个元素的集合开始
            for freqSet in L[i]:
                # 只包含单个元素的集合列表
                H1 = [frozenset([item]) for item in freqSet]    # frozenset({2, 3}) 转换为 [frozenset({2}), frozenset({3})]
                # 如果集合元素大于2个,则需要处理才能获得规则
                if (i > 1):
                    rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf) # 集合元素 集合拆分后的列表 。。。
                else:
                    calcConf(freqSet, H1, supportData, bigRuleList, minConf)
        return bigRuleList
    
    # 对规则进行评估 获得满足最小可信度的关联规则
    def calcConf(freqSet, H, supportData, brl, minConf=0.7):
        prunedH = []  # 创建一个新的列表去返回
        for conseq in H:
            conf = supportData[freqSet]/supportData[freqSet-conseq]  # 计算置信度
            if conf >= minConf:
                print(freqSet-conseq,'-->',conseq,'conf:',conf)
                brl.append((freqSet-conseq, conseq, conf))
                prunedH.append(conseq)
        return prunedH
    
    # 生成候选规则集合
    def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):
        m = len(H[0])
        if (len(freqSet) > (m + 1)): # 尝试进一步合并
            Hmp1 = aprioriGen(H, m+1) # 将单个集合元素两两合并
            Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
            if (len(Hmp1) > 1):    #need at least two sets to merge
                rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)
    
    dataSet = loadDataSet()
    L,suppData = apriori(dataSet,minSupport=0.5)
    rules = generateRules(L,suppData,minConf=0.7)
    # rules = generateRules(L,suppData,minConf=0.5)
    print(rules)

    返回关联规则:

    上面的函数稍微有点复杂,因为使用的数据结构有点多,可能会搞混,所以下面说明一下函数意义。(由于我个人叙述可能不太清楚,所以这里引用作者的原话我觉得更好理解一点,稍微有点详细):

    以上便是引用作者对这三个函数的详细描述,在函数中的具体代码,我也有相关的注释,慢慢来应该能够理解的。

    下面对一个毒蘑菇的例子进行运算,检查一下在实际数据中的反应:

    第一个特征表示有毒或者可以使用。如果有毒则为2,可以食用为1。下一个特征蘑菇形状,有3-8六种可能,下面我们找出毒蘑菇中存在的公共特征:

    mushDatSet = [line.split() for line in open('mushroom.dat').readlines()]
    L,suppData = apriori(mushDatSet,minSupport=0.3)
    print(L)
    for item in L[1]:   # 只查看两个元素的集合
        if(item.intersection('2')): # intersection交集(选出毒蘑菇)
            print(item)

    输出了频繁项集和与毒蘑菇相关的特征:

    以上为Apriori算法构建模型的全部内容,该算法不仅适用于零售行业,同样适用于相同技术的其他行业,如网站流量分析以及医药行业等。

     

    参考书籍:《机器学习实战》

    展开全文
  • 使用Jasper或BIRT等报表工具时,常会碰到一些非常规的统计,用报表工具本身或SQL都难以处理,比如与主表相关的子表分布在多个数据库中,报表要展现这些数据源动态关联的结果。集算器具有结构化强计算引擎,集成简单...
           使用Jasper或BIRT等报表工具时,常会碰到一些非常规的统计,用报表工具本身或SQL都难以处理,比如与主表相关的子表分布在多个数据库中,报表要展现这些数据源动态关联的结果。集算器具有结构化强计算引擎,集成简单,可以协助报表工具方便地实现此类需求。下面通过一个例子来说明多数据源动态关联的实现过程。

          主表org在数据源Master中,org里每条记录对应的子表在不同的数据源中,比如org.org_id=”ORG_S”时,这条记录对应的子表是数据源S_odaURL的User表,org.org_id=”ORG_T”时,这条记录对应的子表是数据源T_odaURL中的User表。子表不止两个,名字都是User,需要和主表动态关联再呈现在报表中。逻辑上的关系如下:


            集算器代码如下:


            A1=Master.query("select * from org where org_id like '"+arg1+"%' ")


            执行SQL,从数据源Master的org表取数据。arg1是来自报表的参数,用来过滤数据,假如arg1=”ORG”则A1的计算结果如下:


            A2: for A1
            依次循环A1中的记录,每次动态关联一个子表,并将关联结果合并在B2中。值得注意的是集算器用自然缩进来表示循环语句的作用范围,即B2-B7,循环体中可用A2来引用循环变量,可用#A2来引用循环计数。


            B2=right(A2.org_id,1)+"_odaURL"
            根据当前记录的org_id字段计算出对应子表的数据源名。第一次循环时,B2的计算结果为” S_odaURL”。


            B3=connect(B2)
            根据名字连接到数据源。注意,A1中的数据源Master已配置为自动连接,可以直接使用,B3中的数据源需要使用函数connect手动连接。


            B4=B3.query("select * from user where org=?",A2.org_id)
            查询数据源B3,按条件取出user表中的数据。


            B5=B4.derive(A2.org_id:org_id,A2.org_manager:manager,A2.org_title:title)
            在子表B4中新增三列,数据来自主表,重命名为org_id、manager、title。第一次循环时,B5计算结果如下:


            B6=B1=B1|B5
            将B5的计算结果合并到B1,运算符”|”等价于函数union。循环结束后B1会存储报表需要的完整数据,如下:


            B7=B3.close()
            关闭数据源连接。


            A8: result B1
            将B1返回给报表工具。集算器对外提供JDBC接口,报表工具会将集算器识别为普通数据库,集成方案请参考相关文档。
     
            对于熟练的程序员,还可以使用集算器的长语句来避免循环语句,使代码更为简洁:


            接下来以BIRT为例设计一张简单的分组表,模板如下:


            需要定义报表参数pVar,用来对应集算器中的参数。预览后可以看到报表结果:


            报表调用集算器的方法和调用存储过程一样,比如将本脚本保存为dynamicDatasource.dfx,则在BIRT的存储过程设计器中可以用call dynamicDatasource (?)来调用。

    展开全文
  • java中引用数据类型有哪些?

    万次阅读 多人点赞 2016-07-18 09:45:37
    Java中有俩种数据类型,其中主要有8中基本数据类型和引用数据类型,除了8中基本数据类型以外都是引用数据类型,8中基本数据类型分别是byte,short,int,long,char,boolean,float,double,具体如下:1、boolean:数据值...
  • LR录的脚本记录客户端发送到服务器端的数据,在脚本回放时按...所以引用关联,通过关联在测试中保持动态值,对脚本录制的数据进行处理。设置: 然后在Tools--&gt;&gt;Recording Options--&gt;&g...
  • 我之前写了一篇博文Objective-C Associative References(关联引用),介绍我在在研究objc runtime的有趣的发现,但当时我并没有意识到这个技术应该使用在何处。在一些实践之后,小结一下有关关联引用的一些相关实践...
  • 在一个业务功能中要求先清空一张基础表(user表)再插入一批新数据。在删除过程中报错为其它表有外键引用,无法删除。于是,查询库中哪些表引用了 user 表中的主键为外键。从 INFORMATION_SCHEMA.KEY_COLUMN_USAGE ...
  • MAPX地图数据与属性数据关联

    千次阅读 2007-04-05 09:18:00
    开发过程中涉及的数据主要包括两个来源:一部分是Geoset中包含的地图数据文件,即Djqh.tab、Djs.tab...MapX支持的外部数据在MapX中可以引用多种类型的外部数据。(1)、 地图数据:如果用户已经购买了或是利用MapInfo创
  • 2使用高级语言为报表自定义数据集,在程序中完成多数据关联运算。上述做法除了对数据库产生影响、增加实现难度外,往往报表性能也不理想。  使用润乾集算报表对异构数据源的有效支持可以很方
  • fineReport同一张报表多个数据关联

    万次阅读 2017-07-12 15:19:17
    我们在做报表的时候经常会遇到这样一个问题,为了提高检索性能,我们可能会把比较长的关联复杂的sql拆成多个sql,单独显示,所有我们就定义多个数据,说白了,每个数据源都是sql查询的字段而已。下边以两个数据源为...
  • Simulink代码生成:数据字典的建立、关联模型

    千次阅读 多人点赞 2020-07-18 20:12:12
    本文介绍如何建立Simulink数据字典,并关联模型。 文章目录1 数据字典的作用2 数据对象的简单概念3 数据对象的管理方式3.1 mat文件或m文件3.2 Excel表格&m脚本3.3 Simulink自带的数据字典文件4 建立和关联...
  • 问题描述:某个基础信息表,与系统中30多个表存在外键关系,当删除基础数据时,需要判断是否已经被用过,如果用过则更改标志位,如果没有用过则直接删除,如何能很好实现这个处理?最好能够自动适应表的变化 问题...
  • 2、把更新B表的update的语句放到AMapper.xml中,这样的话在AMapper.xml做更新B的操作,也就会刷新A的缓存,这样再去select关联查询B表就可以查询到更新数据了。 但是这样仍有问题,就是B表的查询还是存在问题...
  • EF Code First 数据关联

    万次阅读 2013-12-09 23:16:22
    很多情况下我们都不需要特意的去配置,Code First就能通过一些引用属性、导航属性等检测到模型之间的关系,自动为我们生成外键。观察下面的类: View Code public class Destination { public int ...
  • 这样就会在一个项目中依赖引用另一个项目的类库和资源,经常会要在项目中引用其他的关联项目。在发布的时候需要将引入的项目打成jar包放入到需要引用的项目中。 以下以一个真实项目的例子说明如何在eclipse中引用...
  • jemter使用--5、动态数据关联

    千次阅读 2016-07-13 14:50:08
    正则表达式允许用户从服务器响应中获取数据,作为一个后置处理器,正则表达式提取器会在每一个请求执行再执行,正则表达式提取请求的参数值,产生模板字符串,并将结果保存到给出的变量中。 在需要获得数据的请求...
  • 测试工具 - Postman接口测试入门使用手册,Postman如何进行数据关联、自动更新cookies、简单编程 Postman 是在测试领域里非常流行的接口测试工具。 本文介绍该工具从安装,到录制用例,再到可以流畅的进行用例回放的...
  • MySQL多表关联数据同时删除sql语句

    千次阅读 2019-07-25 14:24:15
    DELETE删除多表数据,怎样才能同时删除多个关联表的数据呢?这里做了深入的解释: 代码如下 复制代码 1 delete from t1 where 条件 2 delete t1 from t1 where 条件 3 delete t1 from t1,t2 where 条件 4 delete t1,...
  • weka –Apriori算法 关联规则挖掘实验   一、Apriori算法参数含义 本次共进行了9组实验,使用了weka安装目录data文件夹下的contact-lenses.arff数据。     ToolsàArffViewer,打开contact-...
  • 方案一:上传数据后修复表 hdfs dfs -mkdir -p 分区目录 hdfs dfs -put 分区目录 hive> msck repair table 表名 方案二: 上传数据后添加分区 hdfs dfs -mkdir -p 分区目录 hdfs dfs -put 分区目录 hive>
  • 有时候在excel办公中会遇到,两个sheet相同的一列数据,作为关联(就像数据库的表的外键),其中一个sheet想要获取另一个sheet的其他列数据,这样就用到了vlookup函数,下面演示一下:1.这是222.xlsx文件的“Sheet1...
  • 今天写项目遇到一个问题,就是有两张表A表,B表,还有一张A-B关系表,两张表的关系是通过A-B关系表一一对应的,现在我有个需求就是,我现在从A表出发,条件查询出来A表中跟B表关联数据, 直接上SQL语句 SELECT ...
  • update关联其他表批量更新数据

    千次阅读 2019-08-11 06:20:04
    --并以它进行关联,这样在 select语句里即可引用到要update的表的fields  UPDATE Table1 AS t1  SET (Field1,Field2) = (SELECT Field21, Field22   FROM Table2 t2   INNER JOIN Table3 t3   ON t3.Field31...
  • 这里写自定义目录标题原理新的改变功能快捷键合理的创建标题,有助于目录的...在Excel表格中,引用单元格(包括引用其他工作簿)的数据时,==“显示公式”==的功能可以让Excel直接显示公式和所引用的单元格地址,然后我
  • 使用hibernate 注解配置实体类的关联关系,在many-to-one,one-to-one关联中,一边引用自另一边的属性,如果属性值为某某的数据在数据库不存在了,hibernate默认会抛出异常。解决此问题,加上如下注解就可以了:@Not...
  • 一,引用其它位置的数据 在使用Excel电子表格时,有时会引用其它表格中的数据引用其它表格中数据的情况有三种: 第一种:引用同一工作薄(即同一个文件)中其它工作表中的数据; 第二种:引用同一文件夹下其它...
  • Connection Configuration从数据库读取数据关联变量   by:授客 QQ:1033553122 1. 下载mysql jar包 下载mysql jar包 http://dev.mysql.com/downloads/connector/j/ 网盘下载地址:mysql-...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 375,731
精华内容 150,292
关键字:

引用数据后不再关联