SQL参考

更新时间:
复制为 MD 格式

当您需要将大规模业务数据高效载入图数据库,或希望利用成熟的SQL生态工具进行图数据管理与分析时,可以使用SQL直接对图数据进行增删改查(CRUD)操作。与使用Cypher相比,SQL方式在批量写入场景下可提供高达一个数量级的性能提升(通过批量写入可达10万+ TPS),并能与现有的BI、ETL等工具无缝集成。本文介绍如何使用SQLpolar_age中的节点和边进行管理。

优势

  • 高性能批量写入:通过SQLINSERT语法进行批量操作,写入性能可达10万+TPS,远超单条Cypher写入的效率。

  • SQL生态无缝集成:复用您熟悉的SQL语言和工具链,无需学习新的查询语言,降低了开发和维护成本。

  • 强大的索引与并发支持:可直接利用PostgreSQL成熟的索引(如B-tree、GIN)和并发控制机制(如ON CONFLICT实现UPSERT),高效处理数据。

  • 透明的ID管理:通过_make_graph_id函数,您可以使用业务主键显式地生成和管理图节点的唯一ID,便于数据映射和追溯。

  • 灵活的多模型联合查询:可以直接在SQL中将图数据表与普通关系表进行JOIN操作,实现复杂的多模型融合分析。

  • 支持事务与复杂逻辑:可利用SQL事务(BEGIN...COMMIT)精确控制多个操作步骤,例如在删除节点时同步删除其关联的边,保证数据一致性。

操作说明

以下操作将围绕一个简单的社交关系图展开,该图包含Person类型的节点,以及LikeKnowStudyWith类型的边。

image
graph TD
    A[alice] -->|Like| B(bob)
    B --> |StudyWith| C(cook)
    C --> |Know| A
    B --> |Know| D(duke)
    D --> |Like| C
    D --> |Know| A

准备工作:创建图与标签

在写入任何数据之前,您需要先创建图、节点标签和边标签。这相当于在数据库中定义表的结构(Schema)。

  1. 创建一个名为my_graph的图。

    SELECT ag_catalog.create_graph('my_graph');
  2. my_graph图中创建名为 Person 的节点标签(节点表)。

    SELECT ag_catalog.create_vlabel('my_graph', 'Person');
  3. my_graph图中创建LikeStudyWithKnow三种边标签(边表)。

    SELECT ag_catalog.create_elabel('my_graph', 'Like');
    SELECT ag_catalog.create_elabel('my_graph', 'StudyWith');
    SELECT ag_catalog.create_elabel('my_graph', 'Know');

写入数据(INSERT)

写入节点

INSERT

您可以使用标准的INSERT语句向节点标签表(如"my_graph"."Person")中批量写入节点数据。

  • ID生成:使用 ag_catalog._make_graph_id() 函数,根据图名称、标签名称和节点的业务唯一键(如用户 ID、订单号)生成一个确定性的、唯一的节点ID。

  • 属性:使用JSON字符串并通过::ag_catalog.agtype转换为polar_age的属性格式。

INSERT INTO "<graph_name>"."<node_label_name>" (id, properties)
VALUES 
  (ag_catalog._make_graph_id('<graph_name>', '<node_label_name>', '<node_key_1>'), '<properties_json_1>'::ag_catalog.agtype),
  (ag_catalog._make_graph_id('<graph_name>', '<node_label_name>', '<node_key_2>'), '<properties_json_2>'::ag_catalog.agtype);

参数说明

参数

说明

graph_name

图名称

node_label_name

点的标签名称。

node_key

点的唯一键信息,可以为字符类型或整数类型。

properties_json

JSON格式的属性字符串。

示例

Person节点表中批量写入多个节点。使用INSERT语句,并通过_make_graph_id函数为每个节点生成确定性 ID。

INSERT INTO "my_graph"."Person" (id, properties)
VALUES 
    -- _make_graph_id(图名称, 标签名称, 节点业务唯一键)
    (ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'alice'::cstring), '{"name":"alice", "age": 30}'::ag_catalog.agtype),
    (ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'bob'::cstring), '{"name":"bob", "age": 40}'::ag_catalog.agtype),
    (ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'cook'::cstring), '{"name":"cook", "age": 28}'::ag_catalog.agtype),
    (ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'duke'::cstring), '{"name":"duke", "age": 38}'::ag_catalog.agtype);

UPSERT

如果您希望在插入时若节点已存在则忽略或更新,可以使用ON CONFLICT子句。id列是节点表的主键。

  • 若冲突则忽略(DO NOTHING)

    INSERT INTO "my_graph"."Person" (id, properties)
    VALUES 
        (ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'alice'::cstring), '{"name":"alice", "age": 31}'::ag_catalog.agtype)
    ON CONFLICT(id) DO NOTHING;
  • 若冲突则更新(DO UPDATE):使用agtype_concat函数合并新旧属性,实现对现有属性的追加或修改。

    INSERT INTO "my_graph"."Person" AS v (id, properties)
    VALUES 
        (ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'bob'::cstring), '{"name":"bob", "age": 41}'::ag_catalog.agtype)
    ON CONFLICT(id) DO UPDATE
    SET properties = agtype_concat(v.properties, EXCLUDED.properties),timeline = 'now';

写入边

INSERT

写入关系时,需要指定关系的起始节点 ID (start_id) 和结束节点 ID (end_id)。

  • ID生成:边的ID通常使用ag_catalog._next_graph_id()函数自动生成一个序列值。起始和结束节点的ID则通过_make_graph_id()来定位。

INSERT INTO "<graph_name>"."<edge_label_name>" (id, start_id, end_id, properties)
VALUES (
    ag_catalog._next_graph_id('<graph_name>', '<edge_label_name>'), -- 自动生成边 ID
    ag_catalog._make_graph_id('<graph_name>', '<start_node_label_name>', '<start_node_key>'), -- 起始节点 ID
    ag_catalog._make_graph_id('<graph_name>', '<end_node_label_name>', '<end_node_key>'),   -- 结束节点 ID
    '<properties_json>'::ag_catalog.agtype
);

参数说明

参数

说明

graph_name

图名称

edge_label_name

边的标签名称。

start_node_label_name/start_node_key

起始节点的标签名称和唯一键信息。

end_node_label_name/end_node_key

结束节点的标签名称和唯一键信息。

properties_json

JSON格式的属性字符串。

示例

向图中批量写入多个Like类型的边。

INSERT INTO "my_graph"."Like" (id, start_id, end_id, properties)
VALUES 
(        
    ag_catalog._next_graph_id('my_graph'::name, 'Like'::name), 
    ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'alice'::cstring),
    ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'bob'::cstring),
    '{"from" : "alice", "to" : "bob"}'::ag_catalog.agtype
),
(
    ag_catalog._next_graph_id('my_graph'::name, 'Like'::name), 
    ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'duke'::cstring),
    ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'cook'::cstring),
     '{"from" : "duke", "to" : "cook"}'::ag_catalog.agtype
 );

UPSERT

为防止重复创建关系,您可以在边表的(start_id, end_id)列上创建唯一索引,然后使用ON CONFLICT

  1. 创建唯一索引

    CREATE UNIQUE INDEX ON "my_graph"."Like" USING BTREE(start_id, end_id);
  2. 若冲突则忽略(DO NOTHING)

    INSERT INTO "my_graph"."Like" (id, start_id, end_id, properties)
    VALUES 
    (        
        ag_catalog._next_graph_id('my_graph'::name, 'Like'::name), 
        ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'alice'::cstring),
        ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'bob'::cstring),
        '{"from" : "alice", "to" : "bob"}'::ag_catalog.agtype
    )ON CONFLICT(start_id, end_id) DO NOTHING;
  3. 若冲突则更新(DO UPDATE):使用agtype_concat函数合并新旧属性,实现对现有属性的追加或修改。

    INSERT INTO "my_graph"."Like" AS v (id, start_id, end_id, properties)
    VALUES 
    (        
        ag_catalog._next_graph_id('my_graph'::name, 'Like'::name), 
        ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'alice'::cstring),
        ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'bob'::cstring),
        '{"from" : "alice", "to" : "bob", "relation" : "friends"}'::ag_catalog.agtype
    )ON CONFLICT(start_id, end_id) DO UPDATE
    SET properties = agtype_concat(v.properties, EXCLUDED.properties),timeline = 'now';

更新数据(Update)

使用标准的UPDATE语句更新节点或关系的属性。您可以通过节点ID或属性值来定位要更新的数据。

ID更新

语法说明

UPDATE "<graph_name>"."<node_label_name>" SET properties = '<properties_json>'
WHERE id = ag_catalog._make_graph_id('<graph_name>'::name, '<node_label_name>'::name, '<node_key>'::cstring);

参数说明

参数

说明

graph_name

图名称

node_label_name

点的标签名称。

properties_json

更新的JSON格式属性字符串。

node_key

点的唯一键信息。

示例

  • bob节点的属性完全替换为新的内容。

    UPDATE "my_graph"."Person" SET properties = '{"name" : "bob", "age": 32}'::ag_catalog.agtype,timeline = 'now'
    WHERE id = ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'bob'::cstring);
  • 只更新bob节点的年龄,并添加一个新的城市属性,同时保留原有的name属性。

    UPDATE "my_graph"."Person" SET properties = agtype_concat(properties, '{"age": 32, "city": "London"}'::ag_catalog.agtype),timeline = 'now'
    WHERE id = ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'bob'::cstring);

按属性值更新

语法说明

UPDATE "<graph_name>"."<node_label_name>" SET properties = '<properties_json>'
WHERE agtype_access_operator(VARIADIC ARRAY[properties, '"<att_name>"'::agtype]) = '<value>'::agtype;

参数说明

参数

说明

graph_name

图名称

node_label_name

点的标签名称。

properties_json

更新的JSON格式属性字符串。

att_name

属性键值(Key)。

value

属性值(Value)。

示例

UPDATE "my_graph"."Person" SET properties = '{"name" : "bob", "age": 32}'::ag_catalog.agtype,timeline = 'now'
WHERE agtype_access_operator(VARIADIC ARRAY[properties, '"name"'::agtype]) = '"bob"'::agtype;

查询数据(Read)

使用标准的SELECT语句,查询图中符合特定条件的节点或边。

  • 根据ID查询节点

    SELECT * FROM "my_graph"."Person" WHERE id = ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'alice'::cstring);

    返回结果:

         id        |          properties          |           timeline            
    ---------------+------------------------------+-------------------------------
     3_46056256xxx | {"age": 30, "name": "alice"} | 2026-02-02 02:34:32.282769+00
  • 根据属性值查询节点

    SELECT * FROM "my_graph"."Person" WHERE agtype_access_operator(VARIADIC ARRAY[properties, '"name"'::agtype]) = '"bob"'::agtype;

    返回结果:

         id        |         properties         |           timeline            
    ---------------+----------------------------+-------------------------------
     3_44900751xxx | {"age": 32, "name": "bob"} | 2026-02-02 03:08:45.774535+00

删除数据(Delete)

删除边

可以直接删除边表中的记录。您可以通过起始/结束节点ID或其属性来定位要删除的关系。

ID删除

语法说明

DELETE FROM "<graph_name>"."<edge_label_name>"
WHERE 
  start_id = ag_catalog._make_graph_id('<graph_name>'::name, '<start_node_label_name>'::name, '<start_node_key>'::cstring)
  AND end_id = ag_catalog._make_graph_id('<graph_name>'::name, '<end_node_label_name>'::name, '<end_node_key>'::cstring)

参数说明

参数

说明

graph_name

图名称

edge_label_name

边的标签名称。

start_node_label_name/start_node_key

起始节点的标签名称和唯一键信息。

end_node_label_name/end_node_key

结束节点的标签名称和唯一键信息。

示例

DELETE FROM "my_graph"."Like"
WHERE 
  start_id = ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'alice'::cstring) 
  AND end_id = ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'bob'::cstring);

按属性值删除

语法说明

DELETE FROM "<graph_name>"."<edge_label_name>"
WHERE agtype_access_operator(VARIADIC ARRAY[properties, '"<att_name>"'::agtype]) = '<value>'::agtype;

参数说明

参数

说明

graph_name

图名称

edge_label_name

边的标签名称。

att_name

属性键值(Key)。

value

属性值(Value)。

示例

DELETE FROM "my_graph"."Like"
WHERE agtype_access_operator(VARIADIC ARRAY[properties, '"from"'::agtype]) = '"alice"'::agtype;

删除节点

删除节点时,需要注意其关联的关系(边),这会导致悬挂边问题。

仅删除节点(可能产生悬挂边)

直接删除节点表中的记录。该节点关联的边不会被自动删除。

ID删除

语法说明

DELETE FROM "<graph_name>"."<node_label_name>"
WHERE id = ag_catalog._make_graph_id('<graph_name>'::name, '<node_label_name>'::name, '<node_key>'::cstring);

参数说明

参数

说明

graph_name

图名称

node_label_name

点的标签名称。

node_key

点的唯一键信息。

示例

DELETE FROM "my_graph"."Person" 
WHERE id = ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'bob'::cstring);

按属性值删除

语法说明

DELETE FROM "<graph_name>"."<node_label_name>"
WHERE agtype_access_operator(VARIADIC ARRAY[properties, '"<att_name>"'::agtype]) = '<value>'::agtype;

参数说明

参数

说明

graph_name

图名称

node_label_name

点的标签名称。

att_name

属性键值(Key)。

value

属性值(Value)。

示例

DELETE FROM "my_graph"."Person"
WHERE agtype_access_operator(VARIADIC ARRAY[properties, '"name"'::agtype]) = '"alice"'::agtype;

删除节点及其所有关系(级联删除)

为模拟Cypher DETACH DELETE的行为,在一个事务(BEGIN...COMMIT)中,先从内部视图_ag_label_edge中删除所有与目标节点相关的边,然后再删除节点本身。

语法说明

BEGIN;
-- 删除边
DELETE FROM "<graph_name>"._ag_label_edge
WHERE 
  start_id = ag_catalog._make_graph_id('<graph_name>'::name, '<node_label_name>'::name, '<node_key>'::cstring)
  OR end_id = ag_catalog._make_graph_id('<graph_name>'::name, '<node_label_name>'::name, '<node_key>'::cstring)

-- 删除点
DELETE FROM "<graph_name>"."<node_label_name>"
WHERE id = ag_catalog._make_graph_id('<graph_name>'::name, '<node_label_name>'::name, '<node_key>'::cstring);

COMMIT;

参数说明

参数

说明

graph_name

图名称

node_label_name

点的标签名称。

node_key

点的唯一键信息。

示例

BEGIN;

-- 步骤1:删除所有与'bob'节点关联的边
DELETE FROM "my_graph"._ag_label_edge
WHERE 
    start_id = ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'bob'::cstring)
    OR end_id = ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'bob'::cstring);

-- 步骤2:删除'bob'节点本身
DELETE FROM "my_graph"."Person"
WHERE id = ag_catalog._make_graph_id('my_graph'::name, 'Person'::name, 'bob'::cstring);

COMMIT;

创建索引以优化查询

当您的查询条件总是针对properties字段中的某个特定键时(例如,频繁通过name查找Person)。可以通过创建索引,提升查询性能。polar_age支持利用PostgreSQL的原生索引能力。

说明

GIN索引与属性索引在查询方式上存在显著差异,您可以根据实际使用需求进行选择。

GIN索引

语法说明

使用age_create_gin_index函数构建图中的GIN索引。

age_create_gin_index('<graph_name>')

使用说明

MATCH (:Label {property: value})

示例

  1. my_graph图中所有标签的properties字段创建GIN索引。

    SELECT age_create_gin_index('my_graph');
  2. 简单查询:

    SELECT *
    FROM cypher('my_graph', $$
        MATCH (p:Person {name: 'duke'}) 
        RETURN p.name
    $$) AS (name agtype);

    返回结果:

      name  
    --------
     "duke"

属性索引

语法说明

使用age_create_prop_index函数构建图中的属性索引。

age_create_prop_index('<图名称>',
            '<标签名称>',
            '<属性名称>')

age_create_prop_index('<图名称>',
            '<标签名称>',
            '<属性名称1>',
            '<属性名称2>')

age_create_prop_index('<图名称>',
            '<标签名称>',
            '<属性名称1>',
            '<属性名称2>',
            '<属性名称3>')

使用说明

MATCH (o:Label)-[ ]->()
WHERE o.property = value

示例

  1. my_graph图中所有标签的properties字段创建属性索引。

    -- 为单个属性创建索引
    SELECT age_create_prop_index('my_graph', 'Person', 'name');
    
    -- 为多个属性创建联合索引,适用于 WHERE name = 'alice' AND age = '30' 这样的查询
    SELECT age_create_prop_index('my_graph', 'Person', 'name', 'age');
  2. 简单查询:

    SELECT *
    FROM cypher('my_graph', $$
        MATCH (p1:Person {name: 'duke'})-[:Like]->(p2:Person)
        RETURN p2.name
    $$) AS (name agtype);

    返回结果:

      name  
    --------
     "cook"