查询受差分隐私保护的数据

这个主题帮助分析师对受差分隐私保护的数据(即,受隐私保护的表和视图)运行查询,并理解和调整查询返回的结果。

要对受隐私保护的表执行查询,用户必须拥有该表的 SELECT 权限。

限制

  • 差分隐私支持一部分 Snowflake 数据类型、运算符、查询语法和函数。有关您可以在查询中使用的受支持 SQL 的列表,请参阅 差分隐私 SQL 参考

  • 对隐私保护表的查询需要更长时间,因为 Snowflake 必须运行额外的计算来确定要添加多少噪声。对于基本查询,此延迟至少为 7 秒。复杂的查询(例如下面的查询)可能需要更长的时间:

    • 包含多个联接和子查询的查询。

    • 输出多个结果行的查询,例如,当使用 GROUP BY 子句时,结果会产生数百或数千个组。

  • 在受差分隐私保护的表中,最外层 SELECT 子句中的字段只能应用聚合、GROUP BY 或 DP_INTERVAL_LOW/HIGH。不允许其他操作(例如数学运算和并置)。示例:

    • SELECT key, COUNT(*) AS 'c', DP_INTERVAL_LOW('c') FROM T GROUP BY key

      成功: 未针对 keyCOUNT(*)c 执行不支持的操作。

    • SELECT key, 1 + COUNT(*) AS 'c', DP_INTERVAL_LOW('c') FROM T GROUP BY key

      失败: 针对最外层 SELECT 子句中的字段指定了 1 + COUNT(*)

    • SELECT key, AVG(1 + x) AS 'avg', DP_INTERVAL_LO('avg') FROM T GROUP BY key

      成功: 在外层 SELECT 子句中的 1 + x 之后出现 ``AVG``(一个允许的聚合)。

    • SELECT key, COUNT(x) AS 'c', DP_INTERVAL_LOW('c') FROM (SELECT key, 1 + income AS x FROM table) GROUP BY key

      成功: 在嵌套的 SELECT 子句应用了 1 + income,这是允许的。

查询基础知识

本节讨论针对受隐私保护的表运行时查询时,查询的基本组成部分。它包括:

聚合数据

所有针对受隐私保护的表的查询必须聚合结果,而不是检索单个记录。并不是查询的每个部分都需要使用聚合函数,只要聚合最终结果即可。

除 COUNT 函数外,查询不能对列进行汇总,除非该列有 隐私域

有关支持的聚合列表,请参阅 聚合函数

使用联接

以下各节提供了在差分隐私查询中使用联接的指南:

要了解联接两个受隐私保护的表对隐私域的影响,请参阅 隐私域与联接

联接运算符

每个联接必须是使用单个运算符的相等联接。例如,t1.c1 == t2.c1 受支持的,但 col1 > col2col1 + 10 = col2 不受支持。不支持无条件联接。

联接必须使用 JOIN 运算符。不支持使用 WHERE 语法进行联接。有关联接语法的更多信息,请参阅 实施联接

支持的联接

差分隐私查询中的联接必须是以下之一:

  • INNER

  • { LEFT | RIGHT | FULL } OUTER

  • NATURAL

联接的两侧必须具有相同的查询模式。例如,支持以下联接:

两侧都是标识符

SELECT COUNT(*)
FROM t1 INNER JOIN t2 ON t1.a=t2.a;
Copy

两侧都是子查询

SELECT COUNT(*)
FROM (SELECT a, SUM(b) FROM t1 GROUP BY a) AS g1
    INNER JOIN (SELECT * FROM t2) AS g2
    ON g1.a=g2.a;
Copy

目前不支持将标识符与子查询联接。

有关与联接相关的受支持查询语法的信息,请参阅 查询语法

在联接中使用实体键

在处理受 实体级隐私 保护的表时,您可以通过将实体键列包括为联接键的一部分来尽量减少噪声,特别是当它不会在语义上更改查询时。

例如,考虑以下以客户为实体的表格:

描述

customers

客户目录,其中每行都是一个客户,并且有 customer_id

transactions

客户事务,每个客户可以有多个事务。

transaction_lines

在事务中购买的唯一项目。单个事务中可以有多个行。

如果他们遵循最佳实践,数据提供者已经将数据结构化,使得这些表中的每个表都有实体键 customer_id。对于这个数据架构,每个交易行只能属于一个交易,而每个交易只能属于一个客户。从数据本身来看,这种关系并不明显,因此,如果没有额外的信息,为差分隐私添加的噪声量将高于所需的量。

通过包括实体键 customer_id 作为联接键的一部分,即使它是多余的,您可以将噪声量减至最少。例如,将表 transactionstransaction_lines 联接通常只需要联接键 transaction_id。然而,在 transaction_idcustomer_id 上进行联接将导致更少的噪音。

数据类型和隐私域

当联接两个表时,任一侧的联接键列的数据类型必须相同。对于差分隐私,列的数据类型包括它是否具有 隐私域

例如,如果您有一个受隐私保护的表 transactions 和一个不受保护的表 product_lookup,并且您想在 product_id 上联接它们,则两个表中的 product_id 列必须具有相同的数据类型(例如,字符串)并且必须各自具有隐私域。

为了满足这一要求,分析师的管理员可能需要定义一个隐私域,就像数据提供者定义它们一样。有关如何为表设置隐私域的信息,请参阅 设置隐私域

唯一性要求

联接可能会重复数据行,这可能导致查询结果中添加的噪声量变得无绑定。为了确保受隐私保护的数据在联接中不会重复,受隐私保护的表的联接键(即联接表依据的列)必须在另一个表中仅匹配一条记录。这意味着在与受隐私保护的表联接时,对面的联接键必须删除重复项。

重要

联接的唯一性要求并不总是适用于针对受 实体级隐私 保护的表的查询。对于实体级隐私,必须在聚合之前针对实体键对查询执行删除重复项的操作。只要在联接之后但在聚合之前完成此操作,联接就不需要针对已去除重复项的数据进行。有关满足这些要求的更多信息,请参阅 查询受实体级隐私保护的数据

为了满足联接的唯一性要求,查询可以针对联接列的子集使用 GROUP BY 将重复的行分组到一个结果中。

例如,假设 patients 表受差分隐私保护,而 geo_lookup 表不受保护。分析师希望在 zip_code 上联接这两个表,以便他们可以根据 Statepatients 表进行过滤。为了确保受隐私保护的 patients 表中的记录不重复,查询必须针对联接键对 zip_code 表执行删除重复项操作。此操作必须明确完成,即 使 geo_lookup 表在 zip_code 上已经是唯一的。这可确保 Snowflake 可以正确地考虑隐私。

SELECT COUNT(*)
  FROM patients
  LEFT JOIN (SELECT zip_code, ANY_VALUE(state) AS residence_state
            FROM geo_lookup
            GROUP BY zip_code)
  USING zip_code
  WHERE birth_state = residence_state;
Copy

查询受实体级隐私保护的数据

在配置差分隐私时,大多数数据提供者使用实体键来实现 实体级隐私。当一个表受实体级隐私保护时,如果每个实体可能有无限数量的行,则 Snowflake 不允许对字段进行聚合。这意味着查询必须满足以下要求:

  • 在查询中的某个点,受隐私保护的表必须针对实体键执行删除重复项操作。可以用于删除数据重复项的操作有:

    • COUNT( DISTINCT <entity_key_column> )

    • GROUP BY <entity_key_column>

    • UNION(但 UNION ALL 不可用) – 当仅投射实体键时。

  • 如果联接使用的联接键不是实体键列,则该联接不能通过聚合在删除重复项和最终 SELECT 子句之间发生。

备注

如果数据提供者实现了行级隐私,则联接的删除重复项要求不同。有关这些要求的更多信息,请参阅 唯一性要求

为了帮助说明实体级隐私的要求,假设您有一个受隐私保护的表 patients,其中包含实体键列 patient_id。您还有一个非敏感、不受保护的表 geo_lookup。以下示例展示了一个失败的查询,后面是一个成功的重写版本。

示例:删除重复项

以下查询失败,因为它不符合删除重复项要求。尽管 表``patients`` 在 patient_id 上可能已经是唯一的,但查询失败,因为它没有明确删除重复项。

SELECT COUNT(*)
  FROM patients
  WHERE insurance_type = 'Commercial';
Copy

为了使查询成功,须在实体键列上包含一个独特的计数,以便明确对实体键执行删除重复项。例如:

SELECT COUNT(DISTINCT patient_id)
  FROM patients
  WHERE insurance_type = 'Commercial';
Copy
示例:联接的位置

以下查询失败,尽管它使用了 GROUP BY 子句来满足删除重复项要求。它失败的原因是表正在与另一个表联接,而使用的列不是实体键列。

SELECT AVG(bmi)
  FROM (SELECT patient_id, ANY_VALUE(zip_code) AS zip_code
    FROM patients
    GROUP BY patient_id) AS p
  JOIN geo_lookup AS g
    ON p.zip_code = g.zip_code
  WHERE state='CA';
Copy

要重写查询以使其成功,请在联接后面使用 GROUP BY 子句。不能通过聚合在删除重复项和 SELECT 子句之间进行联接。

SELECT AVG(bmi)
  FROM (SELECT patient_id, ANY_VALUE(bmi) as bmi, ANY_VALUE(state) as state
      FROM patients AS p
      JOIN geo_lookup AS g
        ON p.zip_code = g.zip_code
      GROUP BY patient_id)
  WHERE state='CA';
Copy

执行事务级查询

实体级差分隐私的删除重复项要求不会妨碍您执行事务级查询。然而,您必须首先将数据分组到实体级别,然后对这些组进行聚合。

例如,假设您有一个表 doctor_visits,数据提供商已定义一个实体键 patient_id 以实现实体级隐私。一个事务级的查询可能是:“有多少次看医生不是为了常规检查?”以下是如何编写此查询的示例:

SELECT SUM(num_visits)
  FROM (SELECT SUM((visit_reason<>'Regular checkup')::INT) AS num_visits
        WHERE visit_reason IS NOT NULL
        GROUP BY patient_id)
  WHERE num_visits > 0 AND num_visits < 20;
Copy

子查询通过 patient_id 进行分组以删除数据重复项。聚合列 num_visits 捕获不是为了定期检查而去看医生的每位患者的访问次数。然后,查询在每位患者的列上再次聚合,以获取总访问次数。请注意,外部查询中的 WHERE 子句是必需的,以便 针对数据指定隐私域

备注

虽然这不是一项要求,但在联接受受实体级差分隐私保护的表时,最佳实践是包括实体键列作为联接键的一部分(如果它不会在语义上更改查询)。有关更多信息,请参阅 在联接中使用实体键

理解查询结果

对受隐私保护的表的查询不会返回聚合的确切值。差分隐私在结果中引入 噪声,使其成为实际值的近似值。返回的值与实际值相差很大,足以掩盖个人的数据是否包含在聚合中。这适用于所有查询,但返回受隐私保护的表中总行数的查询除外,例如,SELECT COUNT(*) FROM t

分析师需要能够确定引入结果的噪声是否降低了查询的有用性。Snowflake 使用 噪声区间 来帮助分析师解释结果。噪声区间是一个封闭的数学区间,在大多数情况下,包括聚合的实际值。查询的实际结果在噪声区间内的概率为 95%。

将以下功能添加到查询中,可允许分析师使用噪声区间来决定查询的效用:

  • DP_INTERVAL_LOW – 返回噪声间隔的下限。实际值很可能等于或大于这个数字。

  • DP_INTERVAL_HIGH – 返回噪声间隔的上限。实际值很可能等于或小于这个数字。

要使用这些函数,请在主查询中传入聚合列的别名。例如,以下查询返回 num_claims 列的总和以及该聚合的噪声区间:

SELECT SUM(num_claims) AS sum_claims,
      DP_INTERVAL_LOW(sum_claims),
      DP_INTERVAL_HIGH(sum_claims)
  FROM t1;
Copy

输出可能是:

+--------------+--------------------------------+----------------------------------+
|  sum_claims  |  dp_interval_low("sum_claims") |  dp_interval_high("sum_claims")  |
|--------------+--------------------------------+----------------------------------+
|  50          |  35                            |    75                            |
+--------------+--------------------------------+----------------------------------+

在此示例中,返回值是总和值 50。但分析师也以 95% 的确定性确定,聚合的实际值在 35 到 75 之间。

小技巧

有关可能降低结果中噪声的技术的信息,请参阅

跟踪隐私预算消耗

您可以使用 ESTIMATE_REMAINING_DP_AGGREGATES 函数来估算在当前预算窗口内(即,直到累计隐私损失重置为 0)您还可以运行多少个查询。该估计基于聚合的数量而不是查询。例如,查询 SELECT SUM(age), COUNT(age) FROM T 包含两个聚合函数:SUM(age)COUNT(age)

在执行 ESTIMATE_REMAINING_DP_AGGREGATES 函数时,请确保使用与执行查询相同的确切条件,例如相同的用户、角色和账户。

如果您正在运行一个使用多个表的查询,您应该每个表运行一次 ESTIMATE_REMAINING_DP_AGGREGATES,然后使用最低的 NUMBER_OF_REMAINING_DP_AGGREGATES 值作为估计的使用上限。

以下示例展示了一系列查询如何影响隐私预算限制的消耗(即查询的累积隐私损失)以及剩余聚合的估计数量。

1.初步检查

让我们看看表 my_table 上的隐私预算数字。您从未针对此表运行过任何查询。

SELECT * FROM TABLE(SNOWFLAKE.DATA_PRIVACY.ESTIMATE_REMAINING_DP_AGGREGATES('my_table'));
Copy

到目前为止没有使用预算:

+-----------------------------------+--------------+---------------+--------------+
| NUMBER_OF_REMAINING_DP_AGGREGATES | BUDGET_LIMIT | BUDGET_WINDOW | BUDGET_SPENT |
|-----------------------------------+--------------+---------------+--------------|
|                 996               |     233      |     WEEKLY    |     0.0      |
+-----------------------------------+--------------+---------------+--------------+

2.运行查询

让我们运行一个带有一个聚合函数的查询,并再次检查我们的数字:

SELECT SUM(salary) FROM my_table;

-- results omitted ...

SELECT * FROM TABLE(SNOWFLAKE.DATA_PRIVACY.ESTIMATE_REMAINING_DP_AGGREGATES('my_table'));
Copy

剩余的总聚合调用估计减少了一次,累计隐私损失(预算消耗)增加了。

+-----------------------------------+--------------+---------------+--------------+
| NUMBER_OF_REMAINING_DP_AGGREGATES | BUDGET_LIMIT | BUDGET_WINDOW | BUDGET_SPENT |
|-----------------------------------+--------------+---------------+--------------|
|                 995               |     233      |     WEEKLY    |     0.6      |
+-----------------------------------+--------------+---------------+--------------+

3.使用两个聚合函数运行另一个查询

SELECT SUM(age), COUNT(age) FROM my_table GROUP BY STATE;

-- results omitted ...

SELECT * FROM TABLE(SNOWFLAKE.DATA_PRIVACY.ESTIMATE_REMAINING_DP_AGGREGATES('my_table'));
Copy

预计剩余查询已减少两个。请记住,这只是一个估计。

+-----------------------------------+--------------+---------------+--------------+
| NUMBER_OF_REMAINING_DP_AGGREGATES | BUDGET_LIMIT | BUDGET_WINDOW | BUDGET_SPENT |
|-----------------------------------+--------------+---------------+--------------|
|                 993               |     233      |     WEEKLY    |     1.8      |
+-----------------------------------+--------------+---------------+--------------+

4.重新运行查询

让我们重新运行之前的查询,以显示隐私预算始终是扣减的,即使是在相同的查询上。重复查询每次运行都会造成相同的隐私损失(也就是说,它消耗相同数量的隐私预算)。

SELECT SUM(age), COUNT(age) FROM T GROUP BY STATE;

-- results omitted ...

SELECT * FROM TABLE(SNOWFLAKE.DATA_PRIVACY.ESTIMATE_REMAINING_DP_AGGREGATES('my_table'));
Copy

查询的扣减与之前相同:1.2 单位的隐私损失。

+-----------------------------------+--------------+---------------+--------------+
| NUMBER_OF_REMAINING_DP_AGGREGATES | BUDGET_LIMIT | BUDGET_WINDOW | BUDGET_SPENT |
|-----------------------------------+--------------+---------------+--------------|
|                 991               |     233      |     WEEKLY    |     3.0      |
+-----------------------------------+--------------+---------------+--------------+
语言: 中文