通过聚合策略实施实体级隐私

实体级隐私增强了聚合策略提供的隐私保护。通过实体级隐私保护机制,Snowflake 能够确保每个分组包含最低数量的唯一实体(而不仅仅是最低行数)。

大多数与聚合策略相关的任务和注意事项是相同的,无论您是否在实施实体级隐私。有关使用聚合策略的一般信息,请参阅 聚合策略

关于实体级隐私

实体 是指属于逻辑对象的一组属性(例如,用户简介或家庭信息)。这些属性可以用来识别数据集中的实体。实体级隐私是隐私增强技术 (PET) 的功能,用于保护存储在共享数据集中的实体隐私。实体级隐私可确保查询无法暴露实体的敏感属性,即使这些属性可在多条记录中找到。

为实现实体级隐私保护,Snowflake 允许您指定哪些列用于标识实体(实体键)。这让 Snowflake 能够识别数据集中属于特定实体的所有记录。例如,如果实体键被定义为列 email,则 Snowflake 可以确定 email=joe.smith@example.com 属于同一实体的所有记录。

当您为表定义多个实体时,系统会针对每个实体键分别评估聚合策略。

即使查询中未出现键列,该策略仍将应用于查询。例如,对于应用于实体键 (user_id) 的策略,查询 SELECT age FROM T1 GROUP BY age; 仍将对每个分组中的 user_id 应用 min_group_size 限制,尽管该查询中并未包含 user_id

具有实体级隐私的聚合策略

默认情况下,聚合策略要求分析师运行聚合数据的查询,而不是检索单个行,从而实现 行级隐私。然而,当在多个行中(例如,在包含事务数据的表中)发现某个实体的属性时,行级隐私并不能阻止查询暴露这些属性。

例如,假设流媒体服务 ActonViz 拥有的事务表中包含每个观众在观看节目时的电子邮件地址 (user_id) 和家庭 (household_id)。

user_id

household_id

program_id

watch_time

start_time

dave_sr@example.com

12345

1

29

2023-09-12 09:00

mary@bazco.com

23485

1

30

2023-09-12 09:00

dave_sr@example.com

12345

6

18

2023-09-11 13:00

joe@jupiterlink.com

85456

6

25

2023-09-15 22:00

junior@example.com

12345

5

30

2023-09-13 11:00

ActonViz 可以使用聚合策略来强制广告商将数据聚合到至少包含 2 条记录的组中。这样可防止广告商从单个记录中检索数据(行级隐私)。如果每个观众和家庭在表中只出现一次,那就足以保护他们的隐私。

然而,广告商的查询仍然可以了解有关观众及其家庭的信息。一个查询可以创建一个完全由家庭 12345 的记录组成的组,或者更糟糕的是,一个完全由观众 dave_sr 的记录组成的组。在这两种情况下,组中的记录数量将满足 ActonViz 设定的要求(每组至少 2 条记录)。

具有 实体级隐私的聚合策略

为了实现实体级隐私,Snowflake 允许您在将聚合策略分配给表或视图时指定一个或多个实体键。 在定义实体键之后,对聚合约束表或视图的查询返回的组必须至少包含指定数量的 实体,而不仅仅是指定数量的

在前面的示例中,假设 ActonViz 将 household_id 定义为实体键,因为它唯一地标识了每个家庭。每个家庭的隐私现在得到了增强。在变更之前,一个组可以完全由 household_id = 12345 的记录组成,但现在它必须至少包含两个不同的 household_id 值。

请注意,实体键不一定与表的 主键 相同。在此示例中,表可能使用 user_id 作为主键,因为它唯一地标识了观众。但在这种情况下,ActonViz 想要保护整个家庭的隐私,家庭由多个观众组成,因此他们选择了 household_id 作为实体键。

关于最小组大小

每个聚合策略都指定了最小组大小。在没有实体级隐私的情况下,最小组大小定义了聚合组中必须包含的记录数量。指定实体键时,最小组大小定义了为了出现在最终结果中,该组必须包含的最少 唯一 实体数量。请注意,SUM 和 AVG 等聚合函数返回一个组,而 GROUP BY 列则会为分组列中的每个唯一值返回一个组。

以下列级策略不影响 Snowflake 计算聚合组中是否有足够实体的方式:

  • 聚合策略之后强制执行投影策略。

  • 聚合策略之前强制执行掩码策略。任何聚合函数或策略都适用于掩码数据。

在多次使用名称引用的情况下(例如,在 JOIN 或 UNION 运算符中),Snowflake 分别对每个数据集的每个名称引用强制执行最小组大小。即使引用多次指向同一数据集,这也适用。

通过聚合策略强制实施实体级隐私

要通过聚合策略强制实施实体级隐私,请执行以下操作:

  1. 在执行 CREATE AGGREGATION POLICY 命令以创建聚合策略时,指定每个聚合组中必须包含的实体数量

  2. 将聚合策略分配给表或视图时 定义实体键

指定实体的最小数量

如果使用实体键实现实体级隐私时,则通过 CREATE AGGREGATION POLICY 创建聚合策略的语法不会改变。您仍使用 AGGREGATION_CONSTRAINT 函数的 MIN_GROUP_SIZE 实参来指定最小组大小。定义实体键 后,最小组大小的要求从组中记录的数量变为组中实体的数量。

例如,以下代码创建最小组大小为 5 的聚合策略。只要在将策略分配给表时定义了实体键,每个聚合组就必须至少包含 5 个实体。

CREATE AGGREGATION POLICY my_agg_policy
  AS () RETURNS AGGREGATION_CONSTRAINT ->
  AGGREGATION_CONSTRAINT(MIN_GROUP_SIZE => 5);

有关创建聚合策略的完整详细信息,包括在不同情况下强制执行不同限制的条件聚合策略示例,请参阅 创建聚合策略

定义实体键

You define an entity key for a table when you assign the aggregation policy to the table or view. You can define the entity key when creating a new table or view, or when updating an existing table or view.

为现有表和视图定义实体键

执行 ALTER TABLE ...SET AGGREGATION POLICY 命令或 ALTER VIEW ...SET AGGREGATION POLICY 命令以分配聚合策略时,使用 ENTITY KEY 子句指定表或视图中包含实体的标识属性的列(即实体键)。

例如,要在将聚合策略 my_agg_policy 分配给表 viewership_log 的同时创建实体键,请执行以下操作:

ALTER TABLE viewership_log
  SET AGGREGATION POLICY my_agg_policy
  ENTITY KEY (first_name,last_name);

因为列 first_namelast_name 是实体键,所以聚合策略可以确定 first_name = joelast_name = peterbilt 属于同一实体的所有行。

为现有表和视图定义多个实体键

要为现有表定义多个实体键,您可以通过多次调用添加新键,也可以在单次调用中添加多个键。在表上定义键是累积性的操作;不会覆盖或删除先前定义的键。

通过两次调用添加两个实体键。 第一个键包含两列。

ALTER TABLE transactions ADD AGGREGATION POLICY ap ENTITY KEY (user_id, user_email);
ALTER TABLE transactions ADD AGGREGATION POLICY ap ENTITY KEY (vendor_id);

通过单次调用添加两个实体键

ALTER TABLE transactions ADD AGGREGATION POLICY ap ENTITY KEY (user_id) ENTITY KEY (vendor_id);

为新表和视图定义实体键

执行 CREATE TABLE ...WITH AGGREGATION POLICY 命令或 CREATE VIEW ...WITH AGGREGATION POLICY 命令以分配聚合策略时,使用 ENTITY KEY 子句指定表或视图中包含实体的标识属性的列。

例如,要在分配聚合策略并定义实体键的同时创建新表 t1,请执行以下操作:

CREATE TABLE t1
  WITH AGGREGATION POLICY my_agg_policy
  ENTITY KEY (first_name,last_name);

因为列 first_namelast_name 是实体键,所以聚合策略可以确定 first_name = joelast_name = peterbilt 属于同一实体的所有行。

延迟执行的聚合策略

当查询包含子查询时,Snowflake 会尝试在最内层查询上实施实体聚合策略。如果该查询包含 GROUP BY 子句,且 GROUP BY 列与聚合策略的实体键匹配,则该聚合策略不会应用于该子查询,而是应用于该子查询的父查询。这种延迟会沿着查询链向上传递,直到遇到以下任一情况:不再有任何一组 GROUP BY 列与策略的实体键匹配,或者到达顶级查询;无论哪种情况,聚合策略都将应用于该查询。在一条查询链中,聚合策略仅会应用一次。

例如,假设您有一个实体键为 (name, zipcode) 的聚合策略 my_agg_policy。在以下伪查询中,内层查询的 GROUP BY 集合与 my_agg_policy 的实体键匹配,因此策略被延迟到其父查询执行。尽管 GROUP BY 列也与策略列匹配,但由于父查询是顶级查询,策略将在该层应用。

SELECT age, name, zipcode FROM(                        -- Outermost query: my_agg_policy enforced.
  SELECT name, zipcode FROM T GROUP BY name, zipcode   -- Matches my_agg_policy entity key: my_agg_policy deferred
)
  GROUP BY age, name, zipcode;

请注意,当 GROUP BY 列是实体键列的超集时,也会触发延迟执行,并且仅当 GROUP BY 列匹配时才会延迟策略;聚合函数不会触发延迟。

每个聚合策略都会单独应用于查询中的所有查询块。通过 :doc:`集合运算符 </sql-reference/operators-query>`(例如 UNION)组成的多块查询,将为每个查询块单独评估聚合策略。

延迟执行聚合策略会产生一些实用效果,如以下示例所示。

延迟执行示例

假设您希望将用户按“低消费群体”和“高消费群体”两个类别进行汇总,其中实体定义为 (zipcode, email)。通过延迟执行机制,可以实现如下示例所示的效果。若没有延迟执行机制,内层查询将返回 NULL,因为每个组仅包含一个 (zipcode, email) 实体,当 min_group_size 设置为大于 1 的值时,系统会将这些组过滤掉

WITH bucketed AS (
  SELECT
    CASE
      WHEN SUM(transaction_amount) BETWEEN 0 AND 100 THEN 'low'
      WHEN SUM(transaction_amount) BETWEEN 101 AND 100000 THEN 'high'
    END AS transaction_bucket,
    zipcode,               -- zipcode and email need not appear in the select list, but this lets us compute entity_count below
    email
  FROM my_transactions
  GROUP BY zipcode, email  -- This would not work if it was only GROUP BY zipcode, since the entity key is (zipcode, email)
)
SELECT
  transaction_bucket,
  COUNT(DISTINCT zipcode, email) AS entity_count
FROM
  bucketed
GROUP BY transaction_bucket;

多策略延迟执行

如果一个表包含多个聚合策略,每个聚合策略都将独立评估并可能延迟执行。当您在一个表上设置了多个聚合策略时,请谨慎设计查询语句,因为不同策略在不同查询层级应用可能会导致意外结果。

例如,在包含两个独立聚合策略的表中,若尝试使用嵌套查询将用户分类为高消费和低消费群体,您可能会遇到以下问题:

表 T:

user_id, vendor_id, zipcode, email,         transaction_amount
   1     1001       90000    a@example.com        100
   1     1001       90000    a@example.com         50
   2     2001       90001    b@example.com         12
   2     2001       90001    b@example.com          5
   3     3001       90002    c@example.com         40

聚合策略:

  • user_policymin_group_size = 3,实体键 = (user_id)

  • vendor_policymin_group_size = 2,实体键 = (vendor_id)

通过桶将用户分类为高消费或低消费群体的查询:

WITH amounts AS (
  SELECT
    user_id,
    IFF(SUM(transaction_amount) > 50, 'high', 'low') AS bucket
  FROM T
  GROUP BY user_id -- user_policy is deferred, but vendor_policy is enforced
)
SELECT COUNT(*) FROM amounts GROUP BY bucket

意外结果:

在内层查询中,将强制执行 vendor_policy。每行都按 user_id 分组,而它仅对应一个 vendor_id,这违反了 vendor_policy 的最小组大小要求,因此即使有三个不同的客户属于“高消费”桶,内层查询仍将返回 NULL。

移除实体键约束

要移除单个实体键的聚合策略,请执行以下语句:

-- Drop agg policy ap associated with entity key user_id
ALTER TABLE transactions DROP AGGREGATION POLICY ap ENTITY KEY (user_id)

要移除多个实体键的聚合策略,请分别移除每个策略:

-- Drop the agg policies associated with two separate keys
ALTER TABLE transactions DROP AGGREGATION POLICY ap ENTITY KEY (user_id)
ALTER TABLE transactions DROP AGGREGATION POLICY ap ENTITY KEY (vendor_id)

要移除聚合策略及其所有实体,请在 DROP 语句中省略 ENTITY KEY:

-- Drop agg policy ap from the table entirely
ALTER TABLE transactions DROP AGGREGATION POLICY ap

限制

对于定义了多个实体键或聚合策略的表,适用以下限制:

  • 一个实体键最多只能关联一个策略。尝试为已映射到策略的实体键分配其他策略将导致错误。

  • 同一策略不能同时用于行级隐私和实体级隐私。

  • 最多只能有一个策略用于行级隐私。尝试分配其他策略作为行级聚合策略将导致错误。

查询聚合约束表

查询具有实体键的聚合约束表的要求与查询没有实体键的表的要求相同。有关哪些类型的查询符合这些要求的信息,请参阅 查询要求