聚合策略

聚合策略是架构级对象,用于控制哪种类型的查询可以从表或视图中访问数据。将聚合策略应用于表时,对该表的查询必须将数据聚合为最小大小的组,以便返回结果,从而阻止查询从单个记录返回信息。已分得聚合策略的表或视图称为 聚合约束

概述

Snowflake 的核心功能之一是与其他实体共享数据集的能力。利用聚合策略,提供商(数据所有者)可以控制其数据的用途,即使在与使用者共享数据之后也是如此。具体来说,提供商可以要求表的使用者聚合数据,而不是检索各个记录。

创建聚合策略时,提供商的策略管理员指定最小组大小(即必须聚合到一组中的行的数量)。最小组大小越大,使用者使用查询结果推断出单个记录内容的可能性就越小。

将聚合策略应用于表或视图后,对表或视图的查询必须符合两个要求:

  • 查询必须聚合数据。如果查询使用聚合函数,则该函数必须是 允许的聚合函数 之一。

  • 查询创建的每个组都必须包含至少 X 个记录的聚合,其中 X 是聚合策略的最小组大小。

如果查询返回的组包含的记录少于策略的最小组大小,则 Snowflake 会将这些组组合成一个 剩余组。Snowflake 将聚合函数应用于相应的列,以返回剩余组的值。但是,由于该值是从属于多个组的行计算的,因此 GROUP BY 键列的值是 NULL。例如,如果查询包含子句 GROUP BY state,则剩余组中 state 的值为 NULL。

返回的结果不足以填充剩余组的查询仍然有效,但会在结果的每个字段中返回一个 NULL 值。

限制

  • 如果查询使用显式分组结构,则必须是 GROUP BY 子句。查询不能使用 GROUP BY ROLLUPGROUP BY CUBEGROUP BY GROUPING SETS 等相关结构。

  • 当其中一个查询作用于聚合约束的表时,大多数 集合运算符 不受支持。作为例外,UNION ALL 受支持,但每个结果组都必须达到要查询的聚合约束表的最小组大小(有关详细信息,请参阅 查询要求)。

  • 如果聚合约束表的某列受 投影策略 保护,则对该表的查询不能将此列用作 COUNT 函数的实参。

  • 在对聚合约束的表或视图的查询中,不允许使用 递归 CTEs

  • 在对聚合约束的表或视图的查询中,不允许使用 窗口函数

  • 如果引用符合聚合策略要求的查询部分,或者从该部分进行引用,则对聚合约束表的查询不能使用 相关子查询横向联接。以下示例说明了禁止的查询类型。

    示例 1

    假设 protected_table 受聚合约束,则不允许进行以下查询,因为聚合数据的查询部分在子查询外部引用了该查询的另一部分:

    SELECT c1, c2
    FROM open_table
    WHERE c1 = (SELECT x FROM protected_table WHERE y = open_table.c2);
    
    Copy
    示例 2

    假设 protected_table 受聚合约束,则不允许进行以下查询,因为子查询引用了聚合数据的查询部分(该部分在子查询外部):

    SELECT
      SUM(SELECT COUNT(*) FROM open_table ot WHERE pt.id = ot.id)
    FROM protected_table pt;
    
    Copy

注意事项

在使用聚合策略保护敏感数据时,请考虑以下事项:

  • 聚合策略保护单个记录的数据,而不是实体的数据。如果数据集包含属于同一实体的多个记录,聚合策略只会保护与该实体相关的特定记录的隐私,而不会保护整个实体的隐私。

  • 虽然聚合策略限制了对单个记录的访问,但它们不能保证恶意行为者不会使用特意设计的查询从聚合约束的表中获取可能很敏感的数据。只要尝试足够多次查询,恶意行为者就有可能设法满足聚合要求,从而确定某一行中的值。聚合策略最适合用于已有一定信任度的合作伙伴和客户。此外,提供商应警惕可能存在的数据滥用(例如,查看提供商列表的访问历史记录)。

创建聚合策略

创建聚合策略的语法为:

CREATE [ OR REPLACE ] AGGREGATION POLICY <name>
  AS () RETURNS AGGREGATION_CONSTRAINT -> <body>
  [ COMMENT = '<string_literal>' ];
Copy

其中:

  • name 指定策略的名称。

  • AS () RETURNS AGGREGATION_CONSTRAINT 是策略的签名和返回类型。签名不接受任何实参,返回类型为 AGGREGATION_CONSTRAINT,这是一种内部数据类型。所有聚合策略都具有相同的签名和返回类型。

  • body 是确定聚合策略限制的 SQL 表达式。

从主体调用内部函数

聚合策略的主体使用两个内部函数来定义策略的约束:NO_AGGREGATION_CONSTRAINTAGGREGATION_CONSTRAINT。当主体的条件调用这两个函数之一时,函数的返回值决定了必须如何构建对聚合约束的表或视图进行的查询,以便返回结果。

NO_AGGREGATION_CONSTRAINT

当策略主体从此函数返回值时,查询可以不受限制地从聚合约束的表或视图中返回数据。例如,当管理员需要从聚合约束的表或视图中获取未聚合的结果时,策略主体可以调用此函数。

在无实参的情况下调用 NO_AGGREGATION_CONSTRAINT。

AGGREGATION_CONSTRAINT

当策略主体从此函数返回值时,查询必须聚合数据才能返回结果。使用 MIN_GROUP_SIZE 实参指定每个聚合组中必须包含多少个记录。

AGGREGATION_CONSTRAINT 函数的语法是:

AGGREGATION_CONSTRAINT ( MIN_GROUP_SIZE => <integer_expression> )
Copy

其中 integer_expression 解析为 策略的最小组大小

10 作为实参传递给函数是有区别的。两者都需要聚合结果。

  • 传递 1 还要求每个聚合组至少包含一个来自聚合约束表的记录。因此,对于外部联接,来自聚合约束表的至少一个记录必须匹配来自未受保护表的记录。

  • 传递 0 允许查询返回完全由另一个表中的记录组成的组。因此,对于聚合约束表和未受保护表之间的外部联接,组可以由未受保护表中不匹配聚合约束表中任何记录的记录组成。

备注

聚合策略的主体不能引用用户定义的函数、表或视图。

策略示例

固定的最小组大小

最简单的聚合策略直接调用 AGGREGATION_CONSTRAINT 函数,并定义一个恒定的最小组大小,此大小应用于对表的所有查询。例如,以下命令创建最小组大小为 5 的聚合策略:

CREATE AGGREGATION POLICY my_agg_policy
  AS () RETURNS AGGREGATION_CONSTRAINT -> AGGREGATION_CONSTRAINT(MIN_GROUP_SIZE => 5);
Copy
条件策略

策略管理员可以定义聚合策略的 SQL 表达式,以便不同的查询根据执行查询的用户角色等因素有不同的限制。这种策略可以允许一个用户不受限制地查询表,同时要求其他人聚合结果。

例如,以下聚合策略向具有 ADMIN 角色的用户授予对表的无限制访问权限,同时要求所有其他查询将数据聚合到包含至少 5 行的组中。

CREATE AGGREGATION POLICY my_agg_policy
  AS () RETURNS AGGREGATION_CONSTRAINT ->
    CASE
      WHEN CURRENT_ROLE() = 'ADMIN'
        THEN NO_AGGREGATION_CONSTRAINT()
      ELSE AGGREGATION_CONSTRAINT(MIN_GROUP_SIZE => 5)
    END;
Copy

小技巧

在条件策略中使用上下文函数(如 CURRENT_ROLE)时,可以使用以下策略:

  • 上下文函数返回字符串,因此使用它们进行比较时区分大小写。如果想进行不区分大小写的比较,可以使用 LOWER 将字符串转换为全部小写。

  • 当上下文函数返回某个值时,POLICY_CONTEXT 函数可以帮助您评估策略正文是否返回正确的值。POLICY_CONTEXT 函数根据一个或多个上下文函数的指定值模拟查询结果。

修改聚合策略

可以使用 ALTER AGGREGATION POLICY 命令修改确定聚合策略最小组大小的 SQL 表达式。也可以重命名策略或更改其注释。

在修改聚合策略之前,可以执行 DESCRIBE AGGREGATION POLICY 命令或 GET_DDL 函数,以查看策略的当前 SQL 表达式。确定最小组大小的 SQL 表达式显示在 BODY 列中。

例如,您可以执行以下命令,将聚合策略 my_policy 的 SQL 表达式更改为在所有情况下都要求最小组大小为 2 行:

ALTER AGGREGATION POLICY my_policy SET BODY -> AGGREGATION_CONSTRAINT(MIN_GROUP_SIZE=>2);
Copy

分配聚合策略

创建后,聚合策略可应用于一个或多个表或视图,使它们受聚合约束。一个表或视图只能附加一个聚合策略。

使用 ALTER TABLEALTER VIEW 命令的 SET AGGREGATION POLICY 子句将聚合策略分配给现有表或视图:

ALTER { TABLE | VIEW } <name> SET AGGREGATION POLICY <policy_name> [ FORCE ]
Copy

其中:

  • name 指定表或视图的名称。

  • policy_name 指定聚合策略的名称。

  • FORCE 是一个可选参数,它允许命令将聚合策略分配给已经获分配聚合策略的表或视图。新的聚合策略会以原子方式替换现有聚合策略。

例如,要将策略 my_agg_policy 分配给表 t1,请执行:

ALTER TABLE t1 SET AGGREGATION POLICY my_agg_policy;
Copy

还可以使用 CREATE TABLECREATE VIEW 命令的 WITH 子句在创建时将聚合策略分配给表或视图。例如,要将策略 my_agg_policy 分配给新表,请执行:

CREATE TABLE t1 WITH AGGREGATION POLICY my_agg_policy;
Copy

替换聚合策略

要替换聚合策略,推荐的方法是在单个命令中使用 FORCE 参数分离现有聚合策略并分配新策略。这使您能够以原子方式替换旧策略,而不会留下任何保护漏洞。

例如,要将新的聚合策略分配给已受聚合约束的表,请执行:

ALTER TABLE privacy SET AGGREGATION POLICY agg_policy_2 FORCE;
Copy

您也可以在一条语句 (...UNSET AGGREGATION POLICY) 中从表或视图中分离聚合策略,然后在另一条语句 (...SET AGGREGATION POLICY <name>) 中设置表或视图的新策略。如果选择此方法,在分离一个策略和分配另一个策略之间,表不受聚合策略的保护。在此期间,查询可能会访问敏感数据。

分离聚合策略

使用 ALTER TABLE 或 ALTER VIEW 命令的 UNSET AGGREGATION POLICY 子句从表或视图中分离聚合策略,以便无需聚合数据。聚合策略的名称不是必需的,因为一个表或视图不能附加多个聚合策略。

ALTER {TABLE | VIEW} <name> UNSET AGGREGATION POLICY
Copy

其中:

  • name 指定表或视图的名称。

例如,要从视图 v1 中分离聚合策略,请执行:

ALTER VIEW v1 UNSET AGGREGATION POLICY;
Copy

监控聚合策略

实用做法是,考虑采用两种通用方法来确定如何监控聚合策略的使用情况。

发现聚合策略

您可以使用 AGGREGATION_POLICIES 在共享的 Account Usage 架构中查看 SNOWFLAKE 数据库。此视图是 Snowflake 账户中所有聚合策略的 目录。例如:

SELECT * FROM SNOWFLAKE.ACCOUNT_USAGE.AGGREGATION_POLICIES
ORDER BY POLICY_NAME;
Copy

识别聚合策略引用

POLICY_REFERENCES Information Schema 表函数可以识别聚合策略引用。有两种不同的语法选项:

  1. 为在其上设置了指定聚合策略的每个对象(即表或视图)返回一行:

    USE DATABASE my_db;
    USE SCHEMA information_schema;
    SELECT policy_name,
           policy_kind,
           ref_entity_name,
           ref_entity_domain,
           ref_column_name,
           ref_arg_column_names,
           policy_status
    FROM TABLE(information_schema.policy_references(policy_name => 'my_db.my_schema.aggpolicy'));
    
    Copy
  2. 为分配给名为 my_table 的表的每个策略返回一行:

    USE DATABASE my_db;
    USE SCHEMA information_schema;
    SELECT policy_name,
           policy_kind,
           ref_entity_name,
           ref_entity_domain,
           ref_column_name,
           ref_arg_column_names,
           policy_status
    FROM TABLE(information_schema.policy_references(ref_entity_name => 'my_db.my_schema.my_table', ref_entity_domain => 'table'));
    
    Copy

查询要求

在聚合策略应用于表或视图后,对该表或视图的查询必须符合某些要求。本部分讨论对聚合约束表或视图的查询中允许和不允许的内容。

备注

一旦查询的某个部分正确地聚合了数据以满足聚合策略的要求,这些查询限制就不适用,而且查询的其他部分可以包含本应被禁止的内容。

例如,以下查询可以使用不聚合结果的 SELECT 语句,因为查询的其他部分已满足分配给 protected_table 的策略的聚合要求:

SELECT * FROM open_table ot WHERE ot.a > (SELECT SUM(id) FROM protected_table pt)
Copy

有关查询中可包含内容的其他限制,请参阅 限制

聚合函数

在对聚合约束表的查询中,允许使用以下聚合函数:

一个查询可以包含多个这些允许的聚合函数。如果查询尝试使用不允许的聚合函数,则查询会失败。

分组语句

对聚合约束表的查询必须将数据聚合到最小大小的组中。此查询可以使用显式分组语句(即 GROUP BY 子句),或者聚合整个数据集的标量聚合函数(例如 COUNT(*))。

筛选器

一般来说,Snowflake 并不限制查询如何使用 WHERE 和 ON 子句来筛选聚合约束表,前提是查询会聚合筛选器选择的行。

联接

查询可以将聚合约束表与另一个表(包括另一个聚合约束表)进行联接。

Snowflake 会检查每个聚合组,以确保从聚合约束表中提取的行数达到或超过该表的最小组大小。例如,如果最小组大小为 5 的聚合约束表 table_a 与最小组大小为 3 的 table_b 进行联接,则查询返回的每个组都必须使用 table_a 中的至少 5 行和 table_b 中的至少 3 行来创建。

包含联接的查询是否满足聚合约束表的要求取决于从表中提取的行数,而不是组的大小。因此,从联接的数据创建的组的大小可能大于聚合约束表的最小组大小,但仍会导致数据被筛选。例如,假设:

  • agg_t 受聚合约束,最小组大小为 2。此表包含一个具有以下内容的整型列 c:{ 1, 2, 2 }。

  • open_t 不受约束,并包含一个具有以下内容的整型列 c:{ 1, 1, 1, 2 }。

用户执行以下联接两个表的查询:

SELECT c, COUNT(*)
FROM agg_t, open_t
WHERE agg_t.c = open_t.c
GROUP BY agg_t.c;
Copy

查询将返回:

+-----------------+
|  c   | COUNT(*) |
|------+----------|
|  2   |  2       |
|------+----------|
| null |  3       |
+-----------------+

即使第二组有 3 个记录(大于最小组大小),但所有这些记录都对应于聚合约束表中的单个记录,因此值将被筛选掉。

UNION ALL

查询可以使用 UNION ALL 组合两个子查询的结果,即使查询的一个或多个表受聚合约束也是如此。与联接类似,结果中的每个组都必须达到所查询的每个聚合约束表的最小组大小。例如,假设:

  • protected_table1 的最小组大小为 2。

  • protected_table2 的最小组大小为 5。

如果您运行以下查询:

SELECT a, COUNT(*)
FROM (
    SELECT a, b FROM protected_table1
    UNION ALL
    SELECT a, b FROM protected_table2
)
GROUP BY a;
Copy

由键 a 形成的每个组都必须包含 2 个来自 protected_table1 的记录和 5 个来自 protected_table2 的记录,否则这些记录将放在剩余组中。

External Functions

除非查询的另一部分已正确聚合结果以满足聚合约束表的要求,否则查询不能调用 外部函数

记录和指标

查询无法通过 UDF 记录或指标来记录聚合约束表的列。

数据类型转换

在 SELECT 语句中包含数据类型转换函数的查询必须使用该函数的 TRY 版本。例如,允许使用 TRY_CAST 函数,但禁止使用 CAST 函数。数值类型允许使用以下数据类型转换函数:

PIVOT

查询不能对聚合约束表中的列使用 PIVOT 运算符。

扩展示例

创建聚合策略并向表分配聚合策略的一般步骤与创建和分配其他策略(如掩码策略和投影策略)的步骤相同:

  1. 如果您使用的是集中式管理方法,可创建自定义角色(例如 agg_policy_admin)来管理策略。或者,您可以使用现有的角色。

  2. 授予此角色创建和分配聚合策略的权限。

  3. 创建聚合策略。

  4. 将聚合策略分配给表。

将聚合策略分配给表后,对该表的查询必须聚合其数据才能成功。

以下扩展示例深入介绍了此过程的每个步骤 – 从提供商的访问控制管理员创建自定义角色,到数据使用者执行查询来返回聚合的结果。

访问控制管理员任务
  1. 创建自定义角色以管理聚合策略。您也可以重用现有角色。

    USE ROLE USERADMIN;
    
    CREATE ROLE AGG_POLICY_ADMIN;
    
    Copy
  2. agg_policy_admin 自定义角色授予以下权限:在架构中创建聚合策略,并将聚合策略分配给 Snowflake 账户中的表或视图。

    此步骤假定聚合策略将存储在名为 privacy.agg_policies 的数据库和架构中,并且此数据库和架构已存在:

    GRANT USAGE ON DATABASE privacy TO ROLE agg_policy_admin;
    GRANT USAGE ON SCHEMA privacy.agg_policies TO ROLE agg_policy_admin;
    
    GRANT CREATE AGGREGATION POLICY
      ON SCHEMA privacy.agg_policies TO ROLE agg_policy_admin;
    
    GRANT APPLY AGGREGATION POLICY ON ACCOUNT TO ROLE agg_policy_admin;
    
    Copy

    现在,可以将 agg_policy_admin 角色分配给一个或多个用户。

    有关使用聚合策略所需的权限的详细信息,请参阅 权限和命令 (本主题内容)。

聚合策略管理员任务
  1. 创建要求聚合的聚合策略,并定义最小组大小为 3:

    USE ROLE agg_policy_admin;
    USE SCHEMA privacy.aggpolicies;
    
    CREATE AGGREGATION POLICY my_policy
      AS () RETURNS AGGREGATION_CONSTRAINT -> AGGREGATION_CONSTRAINT(MIN_GROUP_SIZE => 3);
    
    Copy
  2. 将聚合策略分配给表 t1

    ALTER TABLE t1 SET AGGREGATION POLICY my_policy;
    
    Copy
使用者查询

一旦提供商共享聚合约束表,数据使用者就可以对它执行查询。对于此示例,假设聚合约束表 t1 包含以下行:

peak

state

elevation

washington

NH

6288

cannon

NH

4080

kearsarge

NH

2937

mansfield

VT

4395

killington

VT

4229

wachusett

MA

2006

现在,假设使用者对 t1 执行以下查询:

SELECT state, AVG(elevation) AS avg_elevation
FROM t1
GROUP BY state;
Copy

结果如下:

+----------+-----------------+
|  STATE   |  AVG_ELEVATION  |
|----------+-----------------+
|  NH      |  4435           |
|  NULL    |  3543           |
+----------+-----------------+

注意,第二组中的 state 值是 NULL,因为该组是将 VTMA 的最高峰海拔取平均值的剩余组。

聚合策略与 Snowflake 功能

以下子部分简要总结了聚合策略如何与各种 Snowflake 功能和服务交互。

其他策略

本部分介绍聚合策略如何与其他策略交互,包括 掩码策略行访问策略投影策略

您可以将其他策略附加到聚合约束表。对表的查询必须满足所有策略的要求才能成功。

如果将行访问策略分配给聚合约束表,则在计算聚合的结果时,不会包括基于行访问策略从查询结果中排除的行。

掩码策略、行访问策略或投影策略的主体不能引用聚合约束表(包括其列)。同样,另一策略的主体不能包含引用聚合约束表的 UDF。

视图和物化视图

您可以向视图和物化视图分配聚合策略。将聚合策略应用于视图时,基础表不会受聚合约束。用户仍然可以不受限制地查询此基表。

为了避免暴露敏感数据的可能性,所有聚合约束视图都被视为 安全视图 (即使它们不是)。

是否可以从聚合约束表创建视图取决于视图的类型:

  • 可以从一个或多个聚合约束表创建常规视图,但是,对该视图的查询必须以符合这些基表限制的方式聚合数据。

  • 您不能基于聚合约束的表或视图创建物化视图,也不能将聚合策略分配给物化视图所基于的表或视图。

克隆对象

以下方法有助于保护数据,防止拥有 SELECT 权限的用户访问存储在克隆数据库或架构中的克隆表或视图:

  • 不支持克隆单个聚合策略对象。

  • 克隆数据库会导致克隆数据库内的所有聚合策略。

  • 克隆架构会导致克隆该架构中的所有聚合策略。

  • 克隆的表会映射到与源表相同的聚合策略。

    • 当表在其父架构克隆的上下文中进行克隆时,如果源表引用了同一父架构中的聚合策略(即本地引用),则克隆的表将引用克隆的聚合策略。

    • 如果源表引用了不同架构中的聚合策略(即外部引用),则克隆的表会保留外部引用。

有关更多信息,请参阅 CREATE <object> ... CLONE

复制

可以使用数据库复制和复制组来复制聚合策略及其分配。

对于 数据库复制,如果满足以下任一条件,则复制操作会失败:

  • 主数据库位于企业账户(或更高版本)中,包含策略,但批准复制的一个或多个账户的版本较低。

  • 主数据库中包含的表或视图具有对其他数据库中的聚合策略的 悬空引用

复制组 中复制多个数据库时,可避免数据库复制的悬空引用行为。

权限和命令

以下子部分提供了有助于管理聚合策略的信息。

聚合策略权限

Snowflake 支持聚合策略对象的以下权限。

请注意,对架构中的对象进行操作还需要对父数据库和架构具有 USAGE 权限。

权限

用途

APPLY

允许在表上对聚合策略执行设置和取消设置操作。

OWNERSHIP

转移聚合策略的所有权,从而授予对聚合策略的完全控制权。更改聚合策略的大多数属性时需要此权限。

有关详细信息,请参阅 DDL 命令、操作和权限总结 (本主题内容)。

聚合策略 DDL 引用

Snowflake 支持通过以下 DDL 创建和管理聚合策略。

DDL 命令、操作和权限总结

下表总结了聚合策略权限和 DDL 操作之间的关系。

请注意,对架构中的对象进行操作还需要对父数据库和架构具有 USAGE 权限。

操作

需要权限

创建聚合策略。

在同一架构中具有 CREATE AGGREGATION POLICY 权限的角色。

更改聚合策略。

具有聚合策略的 OWNERSHIP 权限的角色。

描述聚合策略

以下其中一项:

  • 具有全局 APPLY AGGREGATION POLICY 权限的角色,

  • 具有聚合策略的 OWNERSHIP 权限的角色,

  • 具有聚合策略的 APPLY 权限的角色。

删除聚合策略。

具有聚合策略的 OWNERSHIP 权限的角色。

显示聚合策略。

以下其中一项:

  • 在存在聚合策略的架构中具有 USAGE 权限的角色,

  • 对账户具有 APPLY AGGREGATION POLICY 权限的角色。

设置或取消设置表上的聚合策略。

以下其中一项:

  • 对账户具有 APPLY AGGREGATION POLICY 权限的角色,

  • 对聚合策略具有 APPLY 权限而且对表或视图具有 OWNERSHIP 权限的角色。

Snowflake 支持不同的权限,以创建和设置对象的聚合策略。

  1. 对于集中式聚合策略管理方法,即 aggregation_policy_admin 自定义角色在 所有 表上创建和设置聚合策略时采用的方法,需要以下权限:

    USE ROLE securityadmin;
    GRANT USAGE ON DATABASE mydb TO ROLE agg_policy_admin;
    GRANT USAGE ON SCHEMA mydb.schema TO ROLE proj_policy_admin;
    GRANT CREATE AGGREGATION POLICY ON SCHEMA mydb.schema TO ROLE aggregation_policy_admin;
    GRANT APPLY ON AGGREGATION POLICY ON ACCOUNT TO ROLE aggregation_policy_admin;
    
    Copy
  2. 在混合管理方法中,单个角色具有 CREATE AGGREGATION POLICY 权限,以确保聚合策略的命名一致,并且各个团队或角色具有特定聚合策略的 APPLY 权限。

    例如,可以向自定义角色 finance_role 授予权限,以在该角色拥有的表和视图(即该角色具有表或视图的 OWNERSHIP 权限)上设置聚合策略 cost_center

    USE ROLE securityadmin;
    GRANT CREATE AGGREGATION POLICY ON SCHEMA mydb.schema TO ROLE aggregation_policy_admin;
    GRANT APPLY ON AGGREGATION POLICY cost_center TO ROLE finance_role;
    
    Copy
语言: 中文