• Operational Property Graphs到底是个啥?


    Operational Property Graphs,中文通常译为“操作属性图”。

    作为23ai中被官方highlight出的新特性之一,我们先看下官方的原文描述:

    • Operational Property Graphs in SQL

    Developers can now build real-time graph analysis applications against operational
    data directly in the Oracle Database, utilizing its industry leading security, high
    availability and performance capabilities.

    简单说,开发者可以直接在Oracle 23ai中进行实时图分析,而不需要额外的图数据库。

    为了不纯扯概念,更好的直观体验,下面我们直接按照官方文档给出的一个简单示例来动手练习体验下:

    本次测试环境: Oracle Database 23ai(23.4.0.24.05)
    本次用到技术: 原生JSON数据类型、操作属性图。
    本次测试意义: 直观体验Oracle数据库多模、融合的设计理念所带来的便利性。

    • 1.准备测试表和测试数据
    • 2.创建属性图
    • 3.体验SQL查询属性图

    1.准备测试表和测试数据

    这里创建的示例,整体构成了一个基本的数据库结构,测试表 university、persons、students、friendships 分别用于存储大学、人员、学生和朋友关系的数据。
    特别需要注意的是,persons 表中的 person_data 字段是 JSON 类型,用于存储原生 JSON 数据。这也是23ai在其多模能力上的一种体现,使其能够更适合用来构建现代的应用平台。

    -- 1.CREATE TABLE university
    CREATE TABLE university (
        id NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
        name VARCHAR2(10),
        CONSTRAINT u_pk PRIMARY KEY (id));
        
    INSERT INTO university (name) VALUES ('ABC');
    INSERT INTO university (name) VALUES ('XYZ');
    commit;
    
    -- 2.CREATE TABLE persons
    -- 包含存放原生的JSON数据类型字段person_data,这里是存放了这个人的部门信息和岗位角色
    CREATE TABLE persons (
         person_id NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT
         BY 1),
         name VARCHAR2(10),
         birthdate DATE,
         height FLOAT DEFAULT ON NULL 0,
         person_data JSON,
         CONSTRAINT person_pk PRIMARY KEY (person_id)
       );
    
    INSERT INTO persons (name, height, birthdate, person_data)
           VALUES ('John', 1.80, to_date('13/06/1963', 'DD/MM/YYYY'), '{"department":"IT","role":"Software Developer"}');
    INSERT INTO persons (name, height, birthdate, person_data)
           VALUES ('Mary', 1.65, to_date('25/09/1982', 'DD/MM/YYYY'), '{"department":"HR","role":"HR Manager"}');
    INSERT INTO persons (name, height, birthdate, person_data)
           VALUES ('Bob', 1.75, to_date('11/03/1966', 'DD/MM/YYYY'), '{"department":"IT","role":"Technical Consultant"}');
    INSERT INTO persons (name, height, birthdate, person_data)
           VALUES ('Alice', 1.70, to_date('01/02/1987', 'DD/MM/YYYY'), '{"department":"HR","role":"HR Assistant"}');
    commit;
    
    -- 3.CREATE TABLE students
    -- 这里官方文档有个错误,属于低级错误,插入字段数量和具体赋值对应不上,此处已修正
    CREATE TABLE students (
          s_id NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
          s_univ_id NUMBER,
          s_person_id NUMBER,
          subject VARCHAR2(10),
          CONSTRAINT stud_pk PRIMARY KEY (s_id),
          CONSTRAINT stud_fk_person FOREIGN KEY (s_person_id) REFERENCES persons(person_id),
          CONSTRAINT stud_fk_univ FOREIGN KEY (s_univ_id) REFERENCES university(id)
        );
    
    INSERT INTO students(s_univ_id, s_person_id, subject) VALUES (1,1,'Arts');
    INSERT INTO students(s_univ_id, s_person_id, subject) VALUES (1,3,'Music');
    INSERT INTO students(s_univ_id, s_person_id, subject) VALUES (2,2,'Math');
    INSERT INTO students(s_univ_id, s_person_id, subject) VALUES (2,4,'Science');
    commit;
    
    -- 4.CREATE TABLE friendships
    -- 这里 meeting_date 可理解为朋友之间相遇的日期
    CREATE TABLE friendships (
        friendship_id NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
        person_a NUMBER,
        person_b NUMBER,
        meeting_date DATE,
        CONSTRAINT fk_person_a_id FOREIGN KEY (person_a) REFERENCES persons(person_id),
        CONSTRAINT fk_person_b_id FOREIGN KEY (person_b) REFERENCES persons(person_id),
        CONSTRAINT fs_pk PRIMARY KEY (friendship_id)
    );
    
    INSERT INTO friendships (person_a, person_b, meeting_date) VALUES (1, 3, to_date('01/09/2000', 'DD/MM/YYYY'));
    INSERT INTO friendships (person_a, person_b, meeting_date) VALUES (2, 4, to_date('19/09/2000', 'DD/MM/YYYY'));
    INSERT INTO friendships (person_a, person_b, meeting_date) VALUES (2, 1, to_date('19/09/2000', 'DD/MM/YYYY'));
    INSERT INTO friendships (person_a, person_b, meeting_date) VALUES (3, 2, to_date('10/07/2001', 'DD/MM/YYYY'));
    commit;
    

    2.创建属性图

    -- 5.CREATE PROPERTY GRAPH students_graph
    -- 这里是关键的属性图创建:
    CREATE PROPERTY GRAPH students_graph
      VERTEX TABLES (
        persons KEY (person_id)
          LABEL person
            PROPERTIES (person_id, name, birthdate AS dob)
          LABEL person_ht
            PROPERTIES (height),
        university KEY (id)
      )
      EDGE TABLES (
        friendships AS friends
          KEY (friendship_id)
          SOURCE KEY (person_a) REFERENCES persons(person_id)
          DESTINATION KEY (person_b) REFERENCES persons(person_id)
          PROPERTIES (friendship_id, meeting_date),
        students AS student_of
          SOURCE KEY (s_person_id) REFERENCES persons(person_id)
          DESTINATION KEY (s_univ_id) REFERENCES university(id)
          PROPERTIES (subject)
      );
    

    这个对于DBA出身的小伙伴们来说,可能会很陌生,也稍难理解些。所以这里详细解释说明下:
    上面的SQL语句是在Oracle数据库中,直接创建了一个属性图students_graph,具体包含两个顶点表(persons和university)和两个边表(friendships和students)。

    那什么是顶点表?边表又是啥?

    在图数据库中,顶点(Vertex)和边(Edge)是构建图数据模型的基本元素:
    顶点表(Vertex Table):表示图中的实体或对象。
    边表(Edge Table):表示图中的关系或连接,定义了两个顶点之间的关系。

    具体在我们这个例子中,顶点表personsuniversity表示人员和大学。
    边表friendshipsstudents表示人员之间的友谊关系和人员与大学之间的学生关系。

    了解了基本概念后,再回过头来看这个SQL定义的属性图,拆解分析后是不是很清晰了呢?

    • VERTEX TABLES(顶点表)
    -- persons 表:
        KEY (person_id):指定主键。
            LABEL person:给顶点赋予person标签。
                PROPERTIES (person_id, name, birthdate AS dob):指定属性,其中birthdate属性重命名为dob。
            LABEL person_ht:给顶点赋予person_ht标签。
                PROPERTIES (height):指定属性height。
    
    -- university 表:
        KEY (id):指定主键。
    
    • EDGE TABLES(边表)
    -- friendships 表(别名friends):
        KEY (friendship_id):指定主键。
        SOURCE KEY (person_a) REFERENCES persons(person_id):源顶点引用persons表的person_id。
        DESTINATION KEY (person_b) REFERENCES persons(person_id):目标顶点引用persons表的person_id。
        PROPERTIES (friendship_id, meeting_date):指定属性。
    
    -- students 表(别名student_of):
        SOURCE KEY (s_person_id) REFERENCES persons(person_id):源顶点引用persons表的person_id。
        DESTINATION KEY (s_univ_id) REFERENCES university(id):目标顶点引用university表的id。
        PROPERTIES (subject):指定属性。
    

    3.体验SQL查询属性图

    实际使用了Oracle的图表查询语言来查找名为“John”的人的朋友,并返回这些朋友的名字。

    SELECT *
    FROM GRAPH_TABLE ( students_graph
      MATCH (a IS person) -[e IS friends]- (b IS person)
      WHERE a.name = 'John'
      COLUMNS (b.name)
    );
    

    这个SQL同样会让DBA觉得不适应,看起来怪怪的,但对开发人员应该很容易理解了。为了让DBA出身的小伙伴们也能理解,我们继续逐一来解释:

    -- FROM GRAPH_TABLE:
    使用 GRAPH_TABLE 函数来查询图数据。
    
    -- students_graph:
    这是之前创建的属性图的名称。
    
    -- MATCH (a IS person) -[e IS friends]- (b IS person):
    MATCH:定义图的模式。
        (a IS person):匹配标签为 person 的顶点 a。
        -[e IS friends]-:匹配标签为 friends 的边 e,连接顶点 a 和 b。
        (b IS person):匹配标签为 person 的顶点 b。
    
    -- WHERE a.name = 'John':
    过滤条件:顶点 a 的 name 属性为 'John'。
    
    -- COLUMNS (b.name):
    定义返回的列:返回顶点 b 的 name 属性。
    

    当然,除了返回朋友的名字,还可以返回更多信息,比如和这位朋友友谊开始的日期:

    SELECT *
    FROM GRAPH_TABLE ( students_graph
      MATCH (a IS person) -[e IS friends]- (b IS person)
      WHERE a.name = 'John'
      COLUMNS (b.name, e.meeting_date)
    );
    

    只是在COLUMNS条件中,增加了e.meeting_date,是不是咋一看很晦涩难懂,实际理解了,其实跟普通SQL一样的简单清晰呢?

    上面示例的两个SQL的查询结果如下:

    23:02:23 PRIMARY @ORCL -> JINGYU @PDB1> SELECT *
    FROM GRAPH_TABLE ( students_graph
      MATCH (a IS person) -[e IS friends]- (b IS person)
      WHERE a.name = 'John'
      COLUMNS (b.name)
    );23:02:23   2  23:02:23   3  23:02:23   4  23:02:23   5  23:02:23   6
    
    NAME
    ----------
    Mary
    Bob
    
    Elapsed: 00:00:00.00
    23:02:24 PRIMARY @ORCL -> JINGYU @PDB1> SELECT *
    FROM GRAPH_TABLE ( students_graph
      MATCH (a IS person) -[e IS friends]- (b IS person)
      WHERE a.name = 'John'
      COLUMNS (b.name, e.meeting_date)
    );23:02:35   2  23:02:35   3  23:02:35   4  23:02:35   5  23:02:35   6
    
    NAME	   MEETING_D
    ---------- ---------
    Mary	   19-SEP-00
    Bob	   01-SEP-00
    
    Elapsed: 00:00:00.01
    23:02:35 PRIMARY @ORCL -> JINGYU @PDB1>
    

    清理实验环境:
    删除本次测试的表和属性图,方便快速清理环境或重新测试:

    -- 先删除有外键约束的表:
    drop table students purge;
    drop table friendships purge;
    
    -- 再删除其他表:
    drop table university purge;
    drop table persons purge;
    
    -- 删除属性图:
    drop PROPERTY GRAPH students_graph;
    

    参考的官方文档链接:

    注:官方文档给出的例子有些小问题,已在文章中代码部分修正,方便大家直接复制粘贴文中代码来进行快速测试验证。

    最后总结一下本文都涵盖了哪些内容:

    • 例子中提到的原生JSON数据类型,其实还比较容易理解,无非就是把JSON数据存储到对应这个数据类型的字段中。注意这里并没有提到JSON关系二元性,这个话题以后空了再单独研究讨论。

    • 有个细节,创建表语句中使用的自增id,GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),这其实不算是一个很新的功能,但如果读者对Oracle的认知还停留在11g时代,可能还会认为Oracle不支持类似像MySQL这种自增ID的设计,需要序列配合实现。而实际上,早在12c之后就已经支持了这种方式。

    • 介绍并演示操作属性图这个特性。

    回到本文实验的主题,操作属性图(Operational Property Graphs)其实对传统偏管理方向的DBA来说,理解上还是有一些技术挑战的。

    比如开始很可能照例子创建成功了,也不知道如何用,创建出来的属性图想直接查询看到底是个啥内容,发现自己还不会正确的使用语法。

    笔者也是,但静下心来,花些时间看看文档,理解这类新特性之后,就会发现其强大之处,也容易积极推广给有需要的开发人员来使用,实现简化开发,降低使用属性图的门槛,提升工作效率。

  • 相关阅读:
    nodejs+vue+elementui实验室预约管理系统
    卡片排列-第15届蓝桥第二次STEMA测评Scratch真题精选
    项目_游戏|外星人入侵
    工作电压范围宽的国产音频限幅器D2761用于蓝牙音箱,输出噪声最大仅-90dBV
    跨程序共享数据:Android四大组件之内容提供器
    LoRa技术的常见应用有哪些?
    基于SpringCloud+redis+Springsecurity实现的微信小程序外卖系统
    【紫光同创国产FPGA教程】——PDS安装教程
    【33. 0 1 背包问题】
    Spring Cloud Alibaba+saas企业架构技术选型+架构全景业务图 + 架构典型部署方案
  • 原文地址:https://www.cnblogs.com/jyzhao/p/18259821/operational-property-graphs-dao-di-shi-ge-sha