精华内容
下载资源
问答
  • 作者简介Chao,携程资深数据分析经理,关注数据治理、数据仓库和数据分析领域。致力于数据使用效率及价值提升。一、背景携程金融自2017年成立以来,继承了互联网企业“小步快跑,快速迭代”...

    作者简介

     

    Chao,携程资深数据分析经理,关注数据治理、数据仓库和数据分析领域。致力于数据使用效率及价值提升。

    一、背景

    携程金融自2017年成立以来,继承了互联网企业“小步快跑,快速迭代”的基因,一直保持高速发展。不过业务的频繁迭代以及分散性的数据组织架构,给数据治理工作带来了很大的挑战。特别是在指标应用层面,这些挑战更为明显:

    • 业务频繁迭代,数据知识相对于业务模型变更存在一定滞后性,导致不同数据使用人员对业务理解存在较大偏差。

    • 数据团队比较分散,指标建设存在严重冗余,不仅导致资源浪费,并且在口径描述上缺乏一致性管理,导致在指标使用过程中有很多分歧。

    • 标准化规范浮于表面,无法在数据开发的全生命周期实现系统性约束和校验,存在数据质量风险。

    针对以上这些问题,我们参考了业界比较成熟的OneData方法论。OneData提出了数据建设的三个统一:统一指标定义、统一数仓建模,统一开发流程。基于此,我们结合金融独有的组织架构及业务特点,从指标定义标准化、流程管理系统化两个层面进行了设计和实践,以保障数据能有效支撑和驱动业务的高速发展。

      

    二、指标定义标准化:关于指标定义的思考

    数仓建设者对业务的把控程度直接决定了数仓质量的高低,因此在数据建设过程中如何实现数据模型与业务模型的统一,一直是我们思考的重点。之前我们都是通过文档或wiki的形式来梳理并记录业务知识。比如在指标文档中,我们会通过详细的文字描述信息来确认指标口径。但是受限于文档维护者对业务和需求的理解程度以及口语化描述本身的局限性,这种方式很容易带来理解歧义问题。

    通过日常工作总结,我们发现要解决理解歧义问题,必须从两个方面入手:统一收口指标口径描述和标准化定义流程。

    口径定义收口,即做到指标的统一录入和查询功能,保证指标定义逻辑对所有用户是可见的。口径定义收口很容易通过系统实现,但是如何保证指标定义流程的标准化呢?最重要的是要保证指标定义工作可流程化。

    我们总结了数据分析人员日常指标定义的工作流程,发现了其中可以流程化的四个节点,如下图所示:

    我们以“每日IOS端金融APP注册用户数”指标为例对指标的定义过程进行描述:

    • 指标定义的首要流程,需要明确其要量化的业务线和场景。该例指标需量化的是“金融”板块的“拉新”场景。

    • 其次对该业务线场景下所涉及的业务流程(或事件)进行梳理,明确关键业务步骤,并找到该指标所量化的业务节点,即“注册”。

    • 然后是考虑如何对该业务事件进行量化,即明确定义“注册”事件的量化口径是“用户数”。

    • 最后就是对指标进行维度拆解,该例所涉及的维度有两个“每日”和“IOS端”。“每日”按照日期维度对指标进行聚合汇总;“IOS端”是限定注册来源,进行下钻过滤。

    三、流程管理系统化:系统设计实践

    基于以上描述,我们可以看到指标定义流程已经完成了标准化拆解。但在数据标准管理中,通常标准规范相对好制定,而标准落地就比较困难,这大多都是因为数据标准缺乏有效约束造成的。因此我们开始了“指标标准化管理系统”的开发,借助工具化手段将预定义规范约束到指标定义、开发、应用等各个链路。

    在该系统中,我们将指标定义拆解出的四个流程抽象为了"业务板块和数据域"、"业务过程"、“维度管理”、"指标设计"四个模块。下面就依次介绍一下这几个模块的具体实现。

    3.1 业务板块和数据域

    指标体系构建的第一步就是对当前需要分析的业务场景进行抽象,明确其所属场景。业务板块代表有独特业务场景和流程的业务体系。业务板块是一种大的划分,各业务板块之间的业务重叠度极低。数据使用方可根据自身业务的理解和抽象,在所属业务板块下创建的独有的指标体系,数据独立建设。数据域则是某个业务板块下,对一类业务活动的抽象集合,是一个较高层次的数据归类标准,是对企业业务过程进行抽象、提炼、组合的集合,一般与数仓主题层对应,面向业务分析。业务板块和数据域是有效归纳组织业务过程的方式,方便了对指标的快速定位。


    3.2 业务过程

    业务过程可以说是该系统中最核心的模块。通过将一次业务行为事件抽象为业务过程,并在每个业务过程中维护了具体的表和数仓关联了起来,实现了业务模型与数据模型的统一。


    3.2.1 概念

    业务过程代表某个数据域下的不可拆分的事件行为。业务过程代表的是一次业务事件,而且该事件在该数据域下是具体、明确且没有歧义的。业务过程一般用来标识一次业务活动中的关键节点事件。

    业务过程可分为两类:事务型和快照型。事务型业务过程代表一个时点动作,因此都会具有时间属性。比如一次“交易”业务中可能会有:下单,付款,取消,退款,发货等业务过程,相对应的时间属性分别有下单时间、付款时间、退款时间、发货时间等;快照型则表示非事件动作的周期性度量,比如在贷余额、库存等,这种类型的时间属性仅为观察时点。

    3.2.2 数据表映射

    业务过程一般会与数仓维度建模过程中的事实表进行关联,比如:事务型业务过程会对应事务事实表或累计快照事实表,快照型业务过程则一般对应周期快照事实表。业务过程与事实表之间一般为一对一的关系,也有一对多或多对一的特殊情况,比如:多事务事实表和累计快照事实表就会将多个业务过程产生的事实在一张表中表达,因此在构建过程中,不仅需要维护与事实表的关系,还要添加“约束条件”解决此类问题。

    3.2.3 数据流程图

    日常工作中,业务人员总会通过绘制业务流程图来说明整个业务逻辑流向,帮助开发总览业务全貌,厘清业务细节。数据分析师,要做到数据驱动业务发展,不仅需要熟知业务流程,也需要熟知数据流程,即将业务流程转化为数据流程。基于此我们在系统中实现了特定业务场景下的“业务过程”以业务流程图的形式呈现出来,帮助数据使用方更加明确业务过程在整个业务域中所处的节点和与其关联事实表的触发场景。

    3.3 维度管理

    维度是业务过程(或事件)发生时所处的环境,用来反映业务的一类属性。因此,如果要对特定“业务过程”充分描述,就需要满足与其关联的事实表要冗余足够多的维度属性。在当前以Hadoop等大数据框架为主要构建方式的数仓体系中,为了降低事实表使用时的资源消耗,提升计算效率,事实表大多以冗余维度属性的宽表形式存在,但是大量的维度冗余也带来了模型稳定性的降低。因为维度的属性是有可能发生变化的,如果属性已经冗余到事实表中,那么维度属性就与事实一起被记录到事实表中。如果后续维度属性值改变,由于事实表已经生成,事实表的内容基本不会再做改变,这样就会出现已记录的维度属性与真实的维度属性不一致,导致数据错误的情况。因此,维度属性冗余带来的收益与弊端要综合考虑。

    那么我们如何在事实表不冗余属性的基础上充分描述业务过程呢?我们通过在业务过程上构建“衍生属性”和“关联维度”的功能,进行属性的扩充。

    • 衍生属性通过 SQL 表达式对业务过程所关联事实表中已有的事件属性进行二次加工,产生一个新的属性值。

    • 关联维度:在业务过程中通过已有事件属性与维度表关联,将维度表中的属性扩充到该业务过程中。

    业务过程和维度管理的维护,不仅真正实现了数据对业务的准确描述,另外也给数据赋予了“灵魂”,提高了数仓主题表的可懂性和易用性,为其有效推广带来了很大帮助。

    3.4 指标设计

    3.4.1 定义

    指标就是业务运转过程中产生的度量事实,指标设计是为了在企业内外部使指标的命名、计算方式、业务理解达到一致,避免不同部门同一个指标的数据存在理解不一致的情况。指标定义则是针对业务过程,从不同维度的量化过程。这个过程进一步抽象就是包含两部分:量化和维度。

    • 量化:也就是指标的统计规则定义。

    • 维度:则是从事件的不同角度进行细化分析。

    3.4.2 原子指标

    关于指标的量化,我们在系统中提供了“原子指标”的概念,它是基于某一业务过程下的度量,是业务定义中不可再拆解的指标,具有明确的业务含义。原子指标的核心功能就是对指标的聚合逻辑进行了定义。

    3.4.3 派生指标

    原子指标因为没有统计粒度(及承载指标的实体维度),因此也就不会有特定的数据与其对应,而只有在原子指标的基础上添加了粒度等信息,才能和具体数据对应,而这就是“派生指标”。如果说原子指标只是一个抽象的逻辑定义,那么派生指标则具象化后的实际度量值。派生指标是由原子指标、维度属性、限定属性组合而成。之前提到的“每日IOS端金融APP注册用户数”指标就是派生指标,对其进行拆解如下图所示:

    原子指标和派生指标的定义,通过派生指标继承原子指标业务场景、关联事件、统计逻辑、描述信息等元属性,进一步收敛了指标定义口径,提高了指标一致性。如果以软件工程举例:原子指标可以理解为父类。派生指标代表继承“原子指标抽象类”的实体类。原子指标无法直接构造对象,只能通过派生指标构造,但派生指标必须依赖于某原子指标。


    3.4.4 指标逻辑解析

    基于设计即开发的思想,我们可以总结发现在完成“业务过程”、“原子指标”和“派生指标”整个指标定义流程之后,该指标的逻辑便以数据分析师们最熟悉的“统一语言”呈现了出来,通过SQL实现了指标口径的一致性约束。

    • 业务过程定义了指标的数据源表(与其相关的事实表及关联维度表)及星型模型关联关系。

    • 原子指标定义了指标的聚合逻辑(sum/avg/count)。

    • 派生指标定义了指标的分组(group by)和限定(where)逻辑。

    四、实践应用


    4.1 指标项目构建

    完成指标标准化定义后,我们已经实现了指标定义的统一收口,保证了指标的可见、可查、可信,但是还缺少应用层面的统一收口。如果在应用层面没有统一收口,那么前期的指标定义更像是“空中楼阁”,还是无法从实际应用角度解决指标一致性问题。

    如下图,在没有实现应用统一收口的时候(重构前),指标系统只是起到了指标注册和口径描述功能,调度开发时只是会以指标系统的口径为参考,并未完成定义与开发的统一(如“指标B”的建设逻辑还是会冗余在两个JOB中),整个研发过程还是属于“面向需求”的烟囱式,这不仅破坏了数据一致性原则,也缺乏复用能力,增加了资源浪费和后期维护成本。

    因此,我们基于配置即开发的原则,通过系统保证了指标与任务的统一。开发流程由原来的“面向需求开发”转变为“面向指标开发”,实现了指标任务间的解耦,一个指标只对应一个任务,提升了开发运维效率。同时,我们构建了“指标项目”模块。该模块对粒度相同的相关指标进行自定义归纳,完成与需求的一一对应,保证了应用层面的统一收口。


    4.2 指导建模

    上面我们提到,指标的统计逻辑来源于数仓中的事实表和维度表。而这两类表正是数仓维度建模理论的最终呈现。维度建模以分析决策的需求出发构建模型,构建的数据模型为分析需求服务,因此具体建模过程中,需要对哪些业务从哪些维度进行建模,可以通过指标定义倒推出来。基于该系统的指标定义功能,其实就是维度建模的过程。下图为两者对比:

    可以发现,我们在指标定义过程中,数据域定义、业务过程拆解、维度细化等方面完全和维度建模的总线矩阵构建流程相符。另外,由于该系统实现了指标定义sql的展示,因此数据分析人员就可以很容易的找到与其相关的事实表和维度表,并通过业务过程的数据表映射功能,了解事实表的落地场景和含义,非常有利于数仓模型的推广,从而提升主题表的复用性,减少重复计算造成的资源浪费。


    4.3 业务梳理与数据调研

    “懂业务”是数据工作的基石,数仓建设者即是技术专家,也应该是“大半个”业务专家。因为唯有理解业务,才能发挥数据最大的价值,准确的量化和驱动业务。


    但是很多数据分析人员由于“离数据太近,离业务太远”而往往很难厘清数据背后的业务流程。针对这个难题,我们通过指标系统“业务过程”这个模块,帮助数据分析人员去做业务和数据调研,发现业务和数据之间的关联关系,真正做到“用数据描述和驱动业务”。


    五、总结

    数仓工作中最难的部分并非涉及编码的ETL开发工作,而是如何从繁杂的中寻找到准确、可信的数据,并用这些数据来准确的描述业务,或者说将业务流程转化为数据流程。因此,我们遵从“工作流程化、流程工具化”的原则,以系统的形式将这部分工作规范起来,实现了业务数据调研、指标定义与应用的标准化收口,带来了以下收益:

    • 降低了数据调研成本,将调研成果以更为友好的方式呈现出来,让数据人员更懂业务、业务人员轻松了解数据。

    • 指标定义流程与数仓“维度建模”理论高度吻合,实现了业务模型与数据模型的统一。

    • 降低了数据使用成本,减少了指标冗余,提升了数据开发与运维效率。

    团队招聘信息

    我们是携程金融数据团队,负责数据组件及平台开发、数仓建设与治理等工作。在一站式大数据开发与治理平台、一站式机器学习平台、分布式图计算与图存储、联邦学习、实时数仓、数据采集等方向持续不断地深入探索,支撑金融业务的高速发展。


    如果你热爱技术,并渴望不断成长,携程金融期待与你一起腾飞。目前正在招聘的职位:数据平台开发工程师、图存储与计算工程师、BI分析师。

    简历投递邮箱:tech@trip.com,邮件标题:【姓名】-【携程金融】-【投递职位】

    【推荐阅读】

     “携程技术”公众号

      分享,交流,成长

    展开全文
  • 我们还将讨论描述代码重用三个不同维度(杠杆,价值和分类)新指标,并研究利用对象作为评估软件开发工作量和衡量生产率手段可能性。 担心这些软件指标自动收集为改进软件开发项目管理和控制策略成本...
  • 视频监控业务正朝着第三方提供公共系统发展,而标准化是目前面临首要问题。本文描述了公共视频监控系统与终端的标准化现状,分析了在标准化过程中遇到主要技术难点,并阐述了标准化的整体思路。
  • 数据标准化中数据域确定

    千次阅读 2016-03-30 08:28:46
    例如我们可以在域中确定域的描述信息例如类型、长度以及解释信息,从而实现了域某种程度上的约束。 在我看来在数据标准中引入域的概念可能有以下几方面的考虑: 构建一种通用的、标准化的数据格式,这样便于...
    在进行数据标准化的过程中需要确定属性或者实体的域,通过该域可完成属性或者实体的类别划分以及属性的某些类型约束。例如我们可以在域中确定域的描述信息例如类型、长度以及解释信息,从而实现了域某种程度上的约束。
    
    在我看来在数据标准中引入域的概念可能有以下几方面的考虑:
    • 构建一种通用的、标准化的数据格式,这样便于系统内部或者标准化主体之间的数据交互;
    • 构建一种关于相同信息的统一描述,该描述特指业务层面的描述;
    • 构建一种组合或者非组合的通用语或者词汇,从而可以使系统用语在词汇上保持一直;
    • 便于之后构建数据分类,不同类别的划分就像文件夹一样完成了域的归类;
    • 便于规定统一的物理描述,从而形成了统一的物理实现,同时也形成了不同物理实现变换或转化的基础;
    根据上面提到的这些功能我们在引入数据开发的过程中可能会对应的需要注意以下几点:
    • 在进行数据传输以及数据交互的时候使用统一的数据格式,并且该数据格式需要在数据域中进行严格“备案”;
    • 对业务描述需要进行精确、准确描述,同时需要得到业务需求、开发、测试各方的确认;
    • 根据业务的特点以及其他方面的关系形成严格、规范的业务词典,当进行业务或者功能变化时,需要更新相应的业务词典;
    • 对数据标准化之后的词典进行定期的归类与审查,完成数据的定期整理与分析从而有效避免重复、歧义数据的出现;
    • 不同系统之间可能使用的物理结构存在一定差异,在数据词典中可能需要对这部分差异进行详细记录,这样一方面可以提升数据交互能力,同时可以在将来如果做一些数据库或者其他的数据迁移或转换操作是带来一定的便利。
    展开全文
  • NetCDF数据的标准化处理(一)

    千次阅读 2020-04-14 15:56:33
    NetCDF数据的标准化处理(一)   我们在业务应用中经常会碰到NetCDF(network Common Data Form)网络通用数据格式,相信大家对这种数据格式也比较熟悉了,它是一种自定义多维科学数据集,在海洋、气象、环保、...

    NetCDF数据的标准化处理(一)

      我们在业务应用中经常会碰到NetCDF(network Common Data Form)网络通用数据格式,相信大家对这种数据格式也比较熟悉了,它是一种自定义的多维科学数据集,在海洋、气象、环保、地球物理等领域应用非常广泛。

    一、NetCDF数据结构

      NetCDF数据集包含维度、变量和属性三种描述类型,其中变量存储实际数据,是一个N维数组,维度定义了每个变量的维度信息,属性定义了变量或者数据集本身的辅助信息属性,NetCDF可以对海量网格数据进行高效存储、管理、获取和分发等操作,采用nc文件查看工具Panoply我们可以看到nc数据的结构,如下图所示:
    在这里插入图片描述

    二、ArcGIS对NetCDF的数据管理

      对于标准格网的NetCDF数据,可以采用ArcGIS的镶嵌数据集进行入库管理,详细操作见博客“基于ArcGIS镶嵌数据集的环境数据存储与管理”中介绍的方法和流程,如果该nc数据可以被ArcGIS读取,如下图所示,那么在添加了NetCDF文件数据之后,打开"Raster Type Properties"窗口,就能够识别到NetCDF文件中的所有变量,如下图所示
    在这里插入图片描述
    在这里插入图片描述
    否则,如下图所示就会显示空白,那么这个NetCDF文件是不能直接入镶嵌数据集的,那下边我们来介绍简单结构的NetCDF标准化处理方法。
    在这里插入图片描述

    三、简单的NetCDF标准化处理

    因为NetCDF文件的格式不固定,存在很多种不规范的情况,比较常见的是变量命名不规范,如下图所示结构的NetCDF文件,维度west_east、south_north的命名不规范,我们需要将该NetCDF文件进行重写,将变量名称规范为lon、lat。
    在这里插入图片描述

    • 第一步:读取原始NetCDF文件的维度信息
    oldNC=Dataset(oldncfilepath,'r',format='NETCDF4')
    len_lon=len(oldNC.dimensions["west_east"])
    len_lat=len(oldNC.dimensions["south_north"])
    len_z=len(oldNC.dimensions["bottom_top"])
    len_time=len(oldNC.dimensions["Time"])
    
    • 第二步:创建新NetCDF文件的维度
    newNC=Dataset(outnewncfilepath,'w',format='NETCDF4')
    newNC.createDimension("lon",len_lon)
    newNC.createDimension("lat",len_lat)
    newNC.createDimension("z",len_z)
    newNC.createDimension("time",len_time)
    
    • 第三步:创建新NetCDF文件的各个变量
    var_lon=newNC.createVariable("lon","f8",("lon",))
    var_lat=newNC.createVariable("lat","f8",("lat",))
    var_z=newNC.createVariable("z","f8",("z",))
    var_time=newNC.createVariable("time","f8",("time",))
    var_pm25=newNC.createVariable("pm25","f8",("time","z","lat","lon",),fill_value=9.96921E36)
    var_pm10=newNC.createVariable("pm10","f8",("time","z","lat","lon",),fill_value=9.96921E36)
    var_u10=newNC.createVariable("U10","f8",("time","z","lat","lon",),fill_value=9.96921E36)
    var_v10=newNC.createVariable("V10","f8",("time","z","lat","lon",),fill_value=9.96921E36)
    
    • 第四步:定义新NetCDF文件变量的属性信息
    var_lon.units="degree_east"
    var_lon.standard_name = "longitude";
    var_lat.units="degree_north"
    var_lat.standard_name = "latitude";
    #var_time.units = 'hours since 2015-01-01 00:00:00'
    #var_time.calendar = "gregorian"
    
    • 第五步:给新NetCDF文件的变量赋值
    var_lon[:]=oldNC.variables["west_east"][:]
    var_lat[:]=oldNC.variables["south_north"][:]
    var_z[:]=numpy.arange(0,len_z,1)
    var_time[:]=numpy.arange(0,len_time,1)
    var_pm25[:]=oldNC.variables["PM2_5_DRY"][:]
    var_pm10[:]=oldNC.variables["PM10"][:]
    var_u10[:]=oldNC.variables["U"][:][:,:,0:len_lat,0:len_lon]
    var_v10[:]=oldNC.variables["V"][:][:,:,0:len_lat,0:len_lon]
    
    • 第六步:关闭NetCDF文件
    oldNC.close()
    newNC.close()
    

    重新生成的标准化NetCDF文件结构如下图所示:
    在这里插入图片描述
    那么现在我们的NetCDF文件就可以顺利入镶嵌数据集了。

      上述示例的完整代码已经上传至网盘,链接:https://pan.baidu.com/s/1zSMsgBVOT7RXo4TJrK3n2w
    提取码:y4h6,欢迎大家留言,互相交流学习。

      想了解ArcGIS最新的技术动态和环保最新的应用,请关注微信公众号“环保GIS技术与应用”

    展开全文
  • hand标准化

    千次阅读 2013-12-10 15:11:56
    本文档详细描述了在 客户项目开发中SQL/PLSQL一些规范和开发指南,用于指导开发员遵守开发标准、使用开发模板、快速进入开发角色。 本文内容包括: 1、 对业务表维护API模板 2、 并发程序模板 3、 ...

    DO070 Hand_PLSQL_框架_应用指南



    本文档详细描述了在   客户化项目开发中SQL/PLSQL的一些规范和开发指南,用于指导开发员遵守开发标准、使用开发模板、快速进入开发角色。

    本文内容包括:

    1、  对业务表维护API模板

    2、  并发程序的模板

    3、  如何调试代码

     

    使用框架和不使用框架相比,在刚开始使用的时候,会觉得更麻烦。但是习惯了也就不麻烦了。

    问题是:我们为什么要使用框架?要深刻的理解这个问题,我们先看两个故事。这个故事在当前目录的Story 目录下。看完这两个故事,我们可以理解了,如果Oracle  的代码没有Debug功能。我们如何去分析解决问题? 那将是不可设想的。

    我们公司的客户越来越多,客户化开发也越来越多。随之而来的是可能产生的问题也越来越多。我们的Hotline 所接到的问题中,有很大一部分是客户化开发的问题。而解决这些问题的人,一般都不是当初开发的人。而是在hotline 工作的人。可想而知,我们的代码没有Debug 功能,没有Debug的习惯。以后出了问题,让他们怎么查?

    基于以上原因,我们建议我们的代码都要使用我们的开发框架,因为这个框架是一个具备Debug功能的框架。和Oracle  的Debug框架是一致的。写出来的代码风格跟Oracle是一致的。

     

     

    三个Package介绍

    1、  HAND_DEBUG.pck

    2、  HAND_API.pck

    3、  HAND_CONC_UTL.pck

    这三个package都不大,熟读以后有助于我们使用其中的Api.

     

     

    业务表维护模板

    对于业务表,必须提供公开函数来维护,禁止外部程序直接操作表,一般规则如下图所示:

    1、Table API 实现表基本操作:Insert、Lock、Update、Delete。

    2、Private API调用TableAPI来操作表,该层实现验证表的必输字段,同时通过调用其它API提供的Public API来实现维护表的业务逻辑。

    3、Public API是公开的API,供外部程序调用,该层只做对传入参数作简单验证;然后调用Private API。

    4、对于所有的验证,提供对立的Valiate API

    5、对于简单的业务表,如基础数据,该数据只在Form中维护,可以只建立TableAPI。

    6、各程序包的命名规则:

    Table API:表名 + ‘_PKG’    (table api)

    Private API:表名 + ‘_PVT’  (private api)

    Public API:表名 + ‘_PUB’    (public api)

    Valiate API:表名 + ‘_UTL’   (validate api)

     

    对于多个API间相互调用图例:

     

    以下介绍各API的模板和使用方法

    Table API

    该程序包一般包括以下几个过程:insert_row、lock_row、update_row、delete_row

    包头定义:

    PROCEDURE insert_row(

                  x_row_id in out varchar2,

                  x_primary_key in out number,

                  p_field1 in …

                  … );

    PROCEDURE lock_row(

                  p_primary_key in number,

                  p_field1 in …

                  … );

    PROCEDURE update_row(

                  p_primary_key in number,

                  p_field1 in …

                  … );

    PROCEDURE delete_row(

                  p_primary_key in number);

     

    对于表中含有object_version_number,其过程lock_row可简化为

    PROCEDURE lock_row(

                  p_primary_key in number,

                  p_object_version_number in number );

     

     

    包体如下:

    PROCEDURE insert_row(

                  x_row_id in out varchar2,

                  x_primary_key in out number,

                  p_field1 in …

                  … )

    IS

      CURSOR C

      IS SELECT ROWID FROM <TABLE>

           WHERE <primary_key> = p_primary_key;

    BEGIN

      INSERT INTO <TABLE> (

           field1, field2, … )

      VALUES (

           p_field1, p_field2, … );

     

    OPEN c;
        FETCH c INTO x_row_id;
        IF (c%NOTFOUND) THEN
          CLOSE c;
          RAISE NO_DATA_FOUND;
        END IF;
        CLOSE c;

    END insert_row;

     

    PROCEDURE lock_row(

                  p_primary_key in number,

                  p_field1 in …

                  … )

    IS

        CURSOR c1

        IS SELECT field1,field2,…

             from <TABLE>

            where <primary_key> = p_primary_key

            FOR UPDATE OF <primary_key> NOWAIT;

        tlinfo c1%ROWTYPE;

      BEGIN

        OPEN c1;

        FETCH c1 INTO tlinfo;

        IF (c1%NOTFOUND) THEN

          CLOSE c1;

          fnd_message.set_name('FND', 'FORM_RECORD_DELETED');

          app_exception.raise_exception;

        END IF;

        CLOSE c1;

     

    IF (tlinfo.field1 = p_field1 or (tlinfo.field1 is null and p_field1 is null))

      AND (tlinfo.field2 = p_field2 or (tlinfo.field2 is null and p_field2 is null))

       …

    THEN

          NULL;

        ELSE

          fnd_message.set_name('FND', 'FORM_RECORD_CHANGED');

          app_exception.raise_exception;

    END IF;

    END lock_row;

     

    PROCEDURE update_row(

                  p_primary_key in number,

                  p_field1 in …

                  … )

    IS

    BEGIN

        UPDATE <TABLE> SET

                    field1                = p_field1,

                    field1                = p_field1,

                    …

        WHERE <primary_key> = p_primary_key;

     

        IF (SQL%NOTFOUND) THEN

          RAISE NO_DATA_FOUND;

        END IF;

    END update_row;

     

    PROCEDURE delete_row(

                  p_primary_key in number)

    IS

    BEGIN

        DELETE FROM <TABLE>

        WHERE  <primary_key> = p_primary_key;

     

        IF (SQL%NOTFOUND) THEN

          RAISE NO_DATA_FOUND;

        END IF;

    END delete_row;

    对于表中含有object_version_number,其过程lock_row可简化为

    PROCEDURE lock_row(

                  p_primary_key in number,

                  p_object_version_number in number )

        CURSOR c1

        IS SELECT object_version_number

             from <TABLE>

            where <primary_key> = p_primary_key

            FOR UPDATE OF <primary_key> NOWAIT;

        tlinfo c1%ROWTYPE;

      BEGIN

        OPEN c1;

        FETCH c1 INTO tlinfo;

        IF (c1%NOTFOUND) THEN

          CLOSE c1;

          fnd_message.set_name('FND', 'FORM_RECORD_DELETED');

          app_exception.raise_exception;

        END IF;

        CLOSE c1;

     

    IF (tlinfo.object_version_number = p_object_version_number)

    THEN

          NULL;

        ELSE

          fnd_message.set_name('FND', 'FORM_RECORD_CHANGED');

          app_exception.raise_exception;

    END IF;

    END lock_row;

     

     

     

     

    Private API / Public API 的过程模板

    为统一风格、简化程序、提供统一的例外处理和调试功能,该程序包中的API使用以下模板

      PROCEDURE create_temp
            ( p_api_version           IN  NUMBER,
              p_init_msg_list         IN  VARCHAR2 DEFAULT fnd_api.g_false,
              p_commit                IN  VARCHAR2 DEFAULT fnd_api.g_false,
              x_return_status         OUT NOCOPY VARCHAR2,
              x_msg_count             OUT NOCOPY NUMBER,
              x_msg_data              OUT NOCOPY VARCHAR2,
              p_field1                IN  NUMBER,
              x_temp_id               OUT NOCOPY NUMBER )
      IS
        l_api_name        CONSTANT VARCHAR2(30) := 'CREATE_TEMP';
        l_api_version CONSTANT NUMBER    := 1.0;

      BEGIN
        -- start activity to create savepoint, check compatibility
        -- and initialize message list, include debug message hint to enter api

        x_return_status := HAND_API.start_activity(
                               p_pkg_name      => g_pkg_name,
                               p_api_name      => l_api_name,
                               p_api_type      => g_api_type,
                               p_init_msg_list => p_init_msg_list,
                               l_api_version   => l_api_version,
                               p_api_version   => p_api_version);
        -- check if activity started successfully
        IF (x_return_status = fnd_api.g_ret_sts_unexp_error) THEN
           RAISE
    fnd_api.g_exc_unexpected_error;
        ELSIF (x_return_status = fnd_api.g_ret_sts_error) THEN
           RAISE
    fnd_api.g_exc_error;
        END IF;
       
      -- API body
        -- logging parameters
        /*
        IF l_debug = 'Y' THEN
          hand_debug.log('p_xxx : ' || p_xxx,2);
        END IF;
        */

       
        -- Prev create logic (call other public api)
       
        -- Insert the row into the table by calling the table handler.
        /*
        BEGIN
          -- set primary key from sequence
          select temp_s.nextval into l_temp_id from dual;
          Hand_plsql_template_pkg.insert_row(
                         p_temp_id                     => ,
                         p_field1                      => ,
                         p_field2                      => ,
                         p_object_version_number       => ,
                         p_creation_date               => ,
                         p_created_by                  => ,
                         p_last_udpate_date            => ,
                         p_last_updated_by             => ,
                         p_last_udpate_login           =>  );
        EXCEPTION
          WHEN NO_DATA_FOUND THEN
           x_return_status := fnd_api.g_ret_sts_unexp_error;
           HAND_API.set_message( p_app_name => 'CUX',
                                p_msg_name => 'XH_TABLE_HANDLER_ERROR' );
           RAISE fnd_api.g_exc_unexpected_error;
        END;
        */

        -- Post create logic (call other public api)
      -- API end body

        -- end activity, include debug message hint to exit api

        HAND_API.end_activity( p_pkg_name        => g_pkg_name,
                          p_api_name        => l_api_name,
                              p_commit          => p_commit,
                              x_msg_count      => x_msg_count,
                 x_msg_data       => x_msg_data );
      EXCEPTION
        WHEN
    fnd_api.g_exc_error THEN
          x_return_status := HAND_API.handle_exceptions(
                              p_pkg_name  => g_pkg_name,
                              p_api_name  => l_api_name,
                              p_api_type  => g_api_type,
                              p_exc_name  => HAND_API.g_exc_name_error,
                              x_msg_count => x_msg_count,
                              x_msg_data  => x_msg_data);
        WHEN fnd_api.g_exc_unexpected_error THEN
          x_return_status := HAND_API.handle_exceptions(
                              p_pkg_name  => g_pkg_name,
                              p_api_name  => l_api_name,
                              p_api_type  => g_api_type,
                              p_exc_name  => HAND_API.g_exc_name_unexp,
                              x_msg_count => x_msg_count,
                              x_msg_data  => x_msg_data);
        WHEN OTHERS THEN
          x_return_status := HAND_API.handle_exceptions(
                              p_pkg_name  => g_pkg_name,
                              p_api_name  => l_api_name,
                              p_api_type  => g_api_type,
                              p_exc_name  => HAND_API.g_exc_name_others,
                              x_msg_count => x_msg_count,
                              x_msg_data  => x_msg_data);
      END create_temp;

    1、过程必要的参数

    参数名

    类型

    数据类型

    描述

     

     

     

     

    P_API_VERSION

    IN

    NUMBER

    API版本号,用于检查API版本

    P_INIT_MSG_LIST

    IN

    VARCHAR2

    是否初始化消息队列

    P_COMMIT

    IN

    VARCHAR2

    是否自动提交更改

    X_RETURN_STATUS

    OUT

    VARCHAR2

    返回API执行状态

    S      成功

    E     错误

    U     例外

    X_MSG_COUNT

    OUT

    VARCHAR2

    返回错误消息的个数

    X_MSG_DATA

    OUT

    VARCHAR2

    返回第一个错误信息的内容

    其中:

    P_COMMIT:对于无数据更新操作可以不要,对于这类的API,需要将g_api_type设置为NULL,且调用HAND_API.end_activity不要传递p_commit参数。

     

    验证API

    所有的验证API只返回验证的结果状态,验证的错误信息直接加入消息队列。

    当验证不通过时,使用HAND_API.set_message将验证错误信息加入到消息队列。

    验证API可以不处理例外,而由调用者处理。

    以下提供两种处理方式,一个不带例外处理,一个带例外处理。

    不带例外处理:

      PROCEDURE validate_currency_code(

                       p_currency_code            IN VARCHAR2,

                       x_return_status            OUT NOCOPY VARCHAR2)

      IS

        CURSOR c_currencies

        IS SELECT 1

             FROM fnd_currencies

            WHERE currency_code = p_currency_code;

       

        l_dummy          VARCHAR2(1);

      BEGIN

        x_return_status := fnd_api.g_ret_sts_success;

       

        OPEN c_currencies;

        FETCH c_currencies INTO l_dummy;

        IF c_currencies%NOTFOUND THEN

          x_return_status := fnd_api.g_ret_sts_error;

          HAND_API.set_message( p_app_name       => 'CUX',

                               p_msg_name       => 'XH_INVALID_CURRENCY_CODE',

                               p_token1         => 'P_CURRENCY_CODE',

                               p_token1_value   => p_currency_code);

        END IF;

        CLOSE c_currencies;

      END validate_currency_code;

    带例外处理:

      PROCEDURE validate_currency_code1(

                       p_currency_code            IN VARCHAR2,

                       x_return_status            OUT NOCOPY VARCHAR2)

      IS

        CURSOR c_currencies

        IS SELECT 1

             FROM fnd_currencies

            WHERE currency_code = p_currency_code;

       

        l_api_name           CONSTANT VARCHAR2(30) := 'VALIDATE_CURRENCY_CODE1';

        l_dummy         VARCHAR2(1);

      BEGIN

        x_return_status := fnd_api.g_ret_sts_success;

       

        OPEN c_currencies;

        FETCH c_currencies INTO l_dummy;

        IF c_currencies%NOTFOUND THEN

          CLOSE c_currencies;

          HAND_API.set_message( p_app_name       => 'CUX',

                               p_msg_name       => 'XH_INVALID_CURRENCY_CODE',

                               p_token1         => 'P_CURRENCY_CODE',

                               p_token1_value   => p_currency_code);

          RAISE fnd_api.g_exc_error;

        END IF;

        CLOSE c_currencies;

      EXCEPTION

        WHEN fnd_api.g_exc_error THEN

           x_return_status := fnd_api.g_ret_sts_error;

        WHEN fnd_api.g_exc_unexp_error THEN

           x_return_status := fnd_api.g_ret_sts_unexp_error;

        WHEN OTHERS THEN

           IF fnd_msg_pub.check_msg_level(fnd_msg_pub.g_msg_lvl_unexp_error) THEN

                fnd_msg_pub.add_exc_msg( g_pkg_name, l_api_name );

           END IF;

           x_return_status := fnd_api.g_ret_sts_unexp_error;

      END validate_currency_code1;

     

     

     

    并发程序模板

    并发程序的过程第1,2个参数是固定的:

    参数名

    类型

    数据类型

    描述

     

     

     

     

    ERRBUF

    OUT

    VARCHAR2

    并发程序返回错误信息

    RETCODE

    OUT

    VARCHAR2

    并发程序返回状态

    0  成功

    1  警告

    2  错误

     

     

    CREATE OR REPLACE PACKAGE BODY XH_CONC_TEMPLATE_PUB AS

    /*===============================================

      Copyright (C)  Xieheng Co.,Ltd.                        

                  AllRights Reserved                                           

    ================================================

    * ===============================================

    *   PROGRAM NAME:

    *               

    *   DESCRIPTION:

    *               

    *   HISTORY:

    *     1.00   2005-09-26   xxxxxx   Creation

    *                    

    * ==============================================*/

     

      G_PKG_NAME CONSTANT VARCHAR2(30):= 'XH_CONC_TEMPLATE_PUB';

     

      PROCEDURE main(  errbuf            OUT VARCHAR2,

                       retcode           OUT VARCHAR2,

                       p_parameter1      IN  VARCHAR2,

                       p_parameter2      IN  VARCHAR2)

      IS

        l_return_status    VARCHAR2(30);

        l_msg_count        NUMBER;

        l_msg_data         VARCHAR2(2000);

      BEGIN

        retcode := '0';

        -- concurrent header log

        Hand_conc_utl.log_header;

       

        -- convert parameter data type, such as varchar2 to date

        -- l_date := fnd_conc_date.string_to_date(p_parameter1);

     

      -- conc body 

        -- call process api

        process_request(

                    p_init_msg_list   => fnd_api.g_true,

                    p_commit          => fnd_api.g_false,

                    x_return_status   => l_return_status,

                    x_msg_count       => l_msg_count,

                    x_msg_data        => l_msg_data,

                    p_xxx             => p_parameter1

                    p_xxx             => p_parameter2);

        IF l_return_status = fnd_api.g_ret_sts_error THEN

          RAISE fnd_api.g_exc_error;

        ELSIF l_return_status = fnd_api.g_ret_sts_unexp_error THEN

          RAISE fnd_api.g_exc_unexpected_error;

        END IF;

      -- conc end body

     

        -- concurrent footer log

        Hand_conc_utl.log_footer;

       

      EXCEPTION

        WHEN fnd_api.g_exc_error THEN

          Hand_conc_utl.log_message_list;

          retcode := '1';

          errbuf := l_msg_data;

        WHEN fnd_api.g_exc_unexpected_error THEN

          Hand_conc_utl.log_message_list;

          retcode := '2';

          errbuf := l_msg_data;

        WHEN OTHERS THEN

          fnd_msg_pub.add_exc_msg(p_pkg_name => g_pkg_name, p_procedure_name => 'MAIN' );

          Hand_conc_utl.log_message_list;

          retcode := '2';

          errbuf := SQLERRM;

      END main;

     

    END XH_CONC_TEMPLATE_PUB;

     

    代码调试

    使用模板开发的程序包默认具有调试的功能。

    开发员可以在任何代码段内调用HAND_DEBUG.LOG进行调试信息的输出。

    该调试包可以根据预置文件的设置情况,将信息输出到fnd_log_messages 表、服务器文件、并发程序log文件。

    启动调试使用系统标准的日志预置文件进行控制

    预置文件名

    描述

     

     

    FND:启用调试日志

    是否启动调试

    FND:调试日志级别

    调试须设置为过程 或 对帐单

    FND:调试日志模块

    调试客户化程序,设置为%

     

    启动后,日志默认输出到表fnd_log_message中,若需要产生日志文件需要设置一下两个预置文件

    预置文件名

    描述

     

     

    CUX: Debug Log mode

    FILE               输出到日志文件,但并发输出到并发log文件

    FILEONLY   强制输出到日志文件

    TABLE           输出到表fnd_log_messages

    CUX: Debug Log File

    设置服务器文件名,如/usr/tmp/hand.log,则产生文件为hand_<USER>.log文件,<USER>为登录用户名。

     

     

    展开全文
  • 公司基于springcloud架构搭建了一整套后端服务,按业务模块进行服务拆分和数据库隔离,使用了eureka,zuul这些中间件来实现服务访问依赖,接口文档之前一直都是使用文档方式进行维护,无法及时更新。...
  • 从5G场景与需求与网络挑战映射分析入手,梳理了5G 架构发展方向,描述5G 概要级网络架构和基础设施框架,并对不同功能平面架构方案具体展开,提出5G网络核心功能——网络切片,在最后对5G网络标准化推进节奏进行了...
  • 比起对表和字段的描述更基础了一层。本人之前有一段时间专门为几个银行做了数据标准的事情。对此工作深有体会,发现这是一个非常繁琐的工作,而且业务不断发展,不断会有新的数据进入,标准交付物维护难度也非常高。
  • 一、源码描述这是石家庄财政局信息中心标准化管理系统源码,整体功能相对较为完整,界面也比较美观,可以作为学习交流使用或者课程设计使用,需要朋友可以下载看看哦。二、功能介绍该源码具体功能如下:1、文档...
  • 通过这种方式,FIBO可以为描述金融业务的任何数据(例如,电子表格,关系数据库,XML文档)赋予含义。 FIBO是作为Web本体语言(OWL)中的本体开发的。 该语言由万维网联盟(W3C)进行了编码,并且基于描述逻辑。 ...
  • 永安在线-业务安全蓝军测评标准v2020.3.4,业务安全的目标通常不是杜绝攻击,而是将攻击流量的占比控制在可...当前,业务安全问题缺乏一套评估体系,能够数字体系化的描述遭受攻击带来的危害程度及策略实施后的效果等
  • 1.前言在事务处理系统中数据,主要用于记录和查询业务情况。随着数据仓库(DW)技术不断成熟,企业数据...元数据是描述数据仓库内数据结构和建立方法数据,可将其按用途不同分为两类:技术元数据(Technica
  • 2. 数据结构:《中国银保监会银行业金融机构监管数据标准化规范(2019版)》(以下简称《规范》)共包括十个监管主题域、66张数据表、1852个数据项。数据表报送范围及数据项在《规范》中均有说明。 3. 数据来源:...
  • 该分册规范了运营管理系统安全防护策略和安全防护方案,描述了系统架构、业务逻辑及业务数据流,分析了系统存在安全风险,针对安全防护目标,从终端、边界、网络、主机、应用五个层次规范了系统安全防护方案。...
  • 目前,装备保障系统组织结构与业务运行的模型分析处于起步阶段,业务理解、界定与描述缺乏规范、统一的标准;缺少完整的参考模型协助装备保障人员进行系统分析,装备保障业务的组织与实施缺少有效的指导理论。在借鉴...
  • 一、SQL(结构查询语言) SQL分类: 基本需要了解:MySQL(99SQL) 、HQL(HiveSQL)、SparkSQL、ImpalaSQL、Oracle SQL应用: ... Oracle 数据库用于存放公司的业务数据,交易数据。 用也是标准SQ...
  • 公共信息模型CIM是国际电工委员会 IEC TC57 分会(电力系统控制与相关通信) 第13工作组制定一套国际标准,CIM用一套规范化的、面向对象格式描述电力企业中各种实际对象(如厂站、发电机、电力变压器等),通过一...
  • 就像开发中模板设计模式,提供一个标准的版本让我们使用,当无法满足我们特殊需求时,可以通过定制满足我们需求。 这个定制化的功能就是MeterSphere提供断言脚本功能。 2.脚本断言json场景 本篇文章使用...
  • 用例题看下登录,购物车,还有APP和web区别就行。平时面试一般会问及:1、自我...(逻辑:该业务员既销售产品1又销售产品2)4、接口测试是怎么测,写过脚本么5、简单描述最近一个项目情况及自己担当6、做...
  • 为了实现非结构数据ETL处理,分析了数据整合发展现状和业务需求,描述了目前国际流行公共仓库 元模型(CWM)以及在ETL实现中作用,详细分析了结构数据和非结构数据不同特点。针对两种数据差异,提出...
  • 然而,作为一个业务流程建模语言,WS-CDL规范缺乏对应图形标识标准,导致业务人员无法方便地编排流程。为此,设计了一套图形模型标记WS-CDL-N,该标记与WS-CDL一一对应。并在此基础上,使用GMF框架实现了WS-CDL...
  • 1.在业务逻辑层直接通过JDBC API来持久实体域对象,业务逻辑和数据访问耦合...(Java Data Objects)是SUN公司制定的描述对象持久语义的标准API.(由其他公司实现之) 5.CMP模式。在J2EE架构中,CMP(Container-ma
  • 本文侧重讨论了如何使用UML来描述工作流管理系统,如何跟踪从业务流程到面向对象软件设计的描述信息,如何用UML可交互工件来结构项目知识库。 在本文中,我们先来讨论工作流产品的软件设计者和用户对一种通用语言...
  • \\\业务分析人员之间会经常讨论需求的描述格式。在这篇文章中,我将分享我在这方面的经验。我最近有一个项目。在这个项目中,所有需求描述采用了某种标准化格式。这不仅改善了测试人员和开发人员的体验,提高了团队...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 505
精华内容 202
关键字:

标准化业务的描述