用户提供的数据匹配 (UPDM) 会将您收集的关于用户的第一方数据(例如来自您的网站、应用或实体店的信息)与该用户在所有 Google 广告数据(包括 Google 自有自营的数据)中的登录活动相联接。这包括通过 Google Marketing Platform (GMP) 产品购买的数据,例如使用 Display & Video 360 购买的 YouTube 数据。不支持非 Google 自有自营的其他 GMP 产品。
若要使用用户提供的数据匹配这一功能,广告事件必须与 Google 广告数据涉及的已登录用户相关联。
本文档介绍了用户提供的数据匹配功能,并提供了有关设置和使用方面的指南。
私有云匹配概览
若要获得有价值的广告数据分析,通常需要将来自多个来源的数据整合在一起。若要自行构建解决此数据流水线问题的解决方案,需要投入大量时间和工程资源。广告数据中心中的私有云匹配功能提供了一个广告数据中心查询模板,用于在 BigQuery 中创建匹配表,然后该表可在广告数据中心查询中用于将广告数据与第一方数据进行匹配,从而简化了此流程。使用第一方数据丰富查询可以带来更丰富的客户体验,并且更能抵御整个行业的广告跟踪更改。
由于用户提供的数据匹配功能仅适用于 Google 自有自营广告资源中已登录的用户,因此不会受到第三方 Cookie 即将弃用的影响。由于第一方数据比第三方数据更能抵御行业变化,因此可以提供更丰富的数据洞见,从而提高客户互动度。
流程摘要
- 设置数据提取和匹配
- 请确保您的第一方数据位于 BigQuery 中,并且您的服务账号对其拥有读取权限。请参阅设置数据提取。
- 第一方数据提取和匹配
- 您设置第一方数据的格式,并将其上传到 BigQuery 数据集。
- 您可以通过创建 Private Cloud Match 分析查询并设置时间表来发起数据匹配请求。
- Google 会将您的项目中的数据与 Google 自有的数据(包含 Google 的用户 ID 和经过哈希处理的用户提供的数据)联接起来,以构建和更新匹配表。
- 请参阅注入第一方数据
- 广告数据中心内基于匹配数据的持续查询
- 您可以像在广告数据中心内运行常规查询一样,针对匹配表运行查询。请参阅查询匹配的数据。
了解隐私权要求
收集客户数据
使用用户提供的数据匹配功能时,您必须上传第一方数据。这可能是您通过自己的网站、应用和实体店收集的信息,也可能是客户直接与您分享的任何信息。
您必须:
- 确保您的隐私权政策如实披露您会与第三方分享客户数据,以便第三方代表您提供服务,并且在法律有相应要求的情况下,您会在进行此类分享之前征得客户同意
- 仅使用 Google 批准的 API 或界面来上传客户数据
- 遵守所有适用的法律法规,包括所有适用的自我监管条例或行业准则
确认第一方用户同意情况
为确保您能够在广告数据中心内使用您的第一方数据,您必须根据欧盟地区用户意见征求政策和广告数据中心政策,确认您已征得用户同意,可以与 Google 分享从欧洲经济区 (EEA) 最终用户那里收集的数据。此要求适用于每个广告数据中心账号,且每次上传新的第一方数据时都必须更新意见征求结果。任何一个用户都可以代表整个账号进行这项确认。
请注意,适用于分析查询的 Google 服务查询规则,同样也适用于 UPDM 查询。例如,创建匹配表时,您不能对欧洲经济区 (EEA) 境内用户运行跨服务查询。
若要了解如何在广告数据中心内确认用户同意情况,请参阅欧洲经济区用户意见征求要求。
数据大小
为保护最终用户的隐私,用户提供的数据匹配功能会强制执行以下与数据大小有关的要求:
- 您必须在用户名单中上传至少 1,000 条记录。
- 您的列表不得超过记录数量上限。如需了解最高数据流量上限,请与您的 Google 代表联系。
设置数据提取
开始之前,请确保:
- 您的第一方数据必须位于 BigQuery 中。如果您有 VPC-SC 边界,则此第一方数据必须位于 VPC-SC 内。
- 您的广告数据中心服务账号必须拥有对第一方数据的读取权限。
- 您的第一方数据必须采用正确的格式并经过哈希处理。如需了解详情,请参阅下一部分。
除此之外,Private Cloud Match 无需额外的初始配置。如果您可以运行分析查询,则可以运行私有云匹配查询。
提取和匹配第一方数据
设置输入数据的格式
您的数据必须遵循以下格式要求,才能正确匹配:
- 在以下输入字段说明中指明的位置,您必须使用 SHA256 哈希上传。
- 输入字段必须采用字符串格式。例如,如果您将 BigQuery 的 SHA256 哈希函数与 Base64 编码函数 (TO_BASE64) 搭配使用,请使用以下转换:
TO_BASE64(SHA256(user_data))
。 - UPDM 支持 Base64 编码。您必须使第一方数据的编码与广告数据中心查询中使用的解码一致。如果您更改了第一方数据编码,则必须更新广告数据中心查询,以便从同一基础进行解码。以下示例使用 Base64 编码。
用户 ID
- 纯文本
- 哈希处理:无
电子邮件
- 移除开头和结尾处的空格
- 所有字符均须采用小写格式
- 所有电子邮件地址都必须包含域名,例如 gmail.com 或 hotmail.co.jp
- 移除重音符号,例如将 è、é、ê 或 ë 更改为 e
- 移除
gmail.com
和googlemail.com
电子邮件地址中域名前面的所有句点 (.) - 哈希:Base64 编码的 SHA256
有效: TO_BASE64(SHA256("jeffersonloveshiking@gmail.com"))
无效:TO_BASE64(SHA256(" Jéfferson.Lôves.Hiking@gmail.com "))
电话
- 去除空格
- 采用 E.164 格式 - 美国示例:+14155552671,英国示例:+442071838750
- 移除国家/地区代码前的所有特殊字符(“+”除外)
- 哈希:Base64 编码的 SHA256
有效: TO_BASE64(SHA256("+18005550101"))
无效:TO_BASE64(SHA256("(800) 555-0101"))
名字
- 去除空格
- 所有字符均须采用小写格式
- 移除所有前缀,例如“Mrs.先生,女士,博士
- 请勿移除重音符号,例如 è、é、ê 或 ë
- 哈希:Base64 编码的 SHA256
有效: TO_BASE64(SHA256("daní"))
无效:TO_BASE64(SHA256("Mrs. Daní"))
姓氏
- 去除空格
- 所有字符均须采用小写格式
- 移除所有后缀,例如 Jr.、Sr., 2nd, 3rd, II, III, PHD, MD
- 请勿移除重音符号,例如 è、é、ê 或 ë
- 哈希:Base64 编码的 SHA256
有效: TO_BASE64(SHA256("délacruz"))
无效:TO_BASE64(SHA256("dé la Cruz, Jr."))
国家/地区
- 即使您的所有客户数据都来自同一个国家/地区,也请添加国家/地区代码
- 请勿对国家/地区数据进行哈希处理
- 使用 ISO 3166-1 alpha-2 国家/地区代码
- 哈希处理:无
有效: US
无效: United States of America
或 USA
邮编
- 请勿对邮政编码数据进行哈希处理
- 美国邮编和国际邮编均可使用
- 对于美国:
- 可以使用 5 位数的邮政编码,例如 94043
- 也可以使用 5 位基础邮政编码加 4 位扩展邮政编码的格式,例如 94043-1351 或 940431351
- 对于所有其他国家/地区:
- 无需设置格式(无需转换为小写,也不必移除空格和特殊字符)
- 请勿添加扩展邮编
- 哈希处理:无
哈希验证和数据编码
您可以使用以下哈希验证脚本确保数据格式正确无误。
JavaScript
/**
* @fileoverview Provides the hashing algorithm, as well as some valid hashes of
* sample data for testing.
*/
async function hash(token) {
// Removes leading or trailing spaces and converts all characters to lowercase.
const formattedToken = token.trim().toLowerCase();
// Hashes the formatted string using the SHA-256 hashing algorithm.
const hashBuffer = await crypto.subtle.digest(
'SHA-256', (new TextEncoder()).encode(formattedToken));
// Converts the hash buffer to a base64-encoded string and returns it.
const base64Str = btoa(String.fromCharCode(...new Uint8Array(hashBuffer)));
return base64Str;
}
function main() {
// Expected hash for test@gmail.com:
// h5JGBrQTGorO7q6IaFMfu5cSqqB6XTp1aybOD11spnQ=
hash('test@gmail.com').then(result => console.log(result));
// Expected hash for +18005551212:
// YdkRG+0+bZz8G8O1yzWkAmh8TxVGvuBhor1ET73WTEQ=
hash('+18005551212').then(result => console.log(result));
// Expected hash for John: ltljLzY1ZMwwMlIUCc8iqFLyAy7sCZ7VlnwNAAzsYHo=
hash('John').then(result => console.log(result));
// Expected hash for Doe: eZ75KhGvkY4/t0HfQpNPO1aO0tk6wd908bjUGieTKm8=
hash('Doe').then(result => console.log(result));
}
main()
Python
"""Provides the hashing algorithm, as well as some valid hashes of sample data for testing.
Supports: Python 2, Python 3
Sample hashes:
- Email 'test@gmail.com': h5JGBrQTGorO7q6IaFMfu5cSqqB6XTp1aybOD11spnQ=
- Phone '+18005551212': YdkRG+0+bZz8G8O1yzWkAmh8TxVGvuBhor1ET73WTEQ=
- First name 'John': ltljLzY1ZMwwMlIUCc8iqFLyAy7sCZ7VlnwNAAzsYHo=
- Last name 'Doe': eZ75KhGvkY4/t0HfQpNPO1aO0tk6wd908bjUGieTKm8=
"""
import base64
import hashlib
def hash(token):
# Generates a base64-encoded SHA-256 hash of a normalized input string.
return base64.b64encode(
hashlib.sha256(
token.strip().lower().encode('utf-8')).digest()).decode('utf-8')
def print_hash(token, expected=None):
# Computes and displays the hash of a token, with optional validation.
hashed = hash(token)
if expected is not None and hashed != expected:
print(
'ERROR: Incorrect hash for token "{}". Expected "{}", got "{}"'.format(
token, expected, hashed))
return
print('Hash: "{}"\t(Token: {})'.format(hashed, token))
def main():
# Tests the hash function with sample tokens and expected results.
print_hash(
'test@gmail.com', expected='h5JGBrQTGorO7q6IaFMfu5cSqqB6XTp1aybOD11spnQ=')
print_hash(
'+18005551212', expected='YdkRG+0+bZz8G8O1yzWkAmh8TxVGvuBhor1ET73WTEQ=')
print_hash('John', expected='ltljLzY1ZMwwMlIUCc8iqFLyAy7sCZ7VlnwNAAzsYHo=')
print_hash('Doe', expected='eZ75KhGvkY4/t0HfQpNPO1aO0tk6wd908bjUGieTKm8=')
if __name__ == '__main__':
main()
Go
/*
Provides the hashing algorithm, as well as some valid hashes of sample data for testing.
Sample hashes:
- Email 'test@gmail.com': h5JGBrQTGorO7q6IaFMfu5cSqqB6XTp1aybOD11spnQ=
- Phone '+18005551212': YdkRG+0+bZz8G8O1yzWkAmh8TxVGvuBhor1ET73WTEQ=
- First name 'John': ltljLzY1ZMwwMlIUCc8iqFLyAy7sCZ7VlnwNAAzsYHo=
- Last name 'Doe': eZ75KhGvkY4/t0HfQpNPO1aO0tk6wd908bjUGieTKm8=
*/
package main
import (
"crypto/sha256"
"encoding/base64"
"fmt"
"strings"
)
// Hash hashes an email, phone, first name, or last name into the correct format.
func Hash(token string) string {
formatted := strings.TrimSpace(strings.ToLower(token))
hashed := sha256.Sum256([]byte(formatted))
encoded := base64.StdEncoding.EncodeToString(hashed[:])
return encoded
}
// PrintHash prints the hash for a token.
func PrintHash(token string) {
fmt.Printf("Hash: \"%s\"\t(Token: %s)\n", Hash(token), token)
}
func main() {
PrintHash("test@gmail.com")
PrintHash("+18005551212")
PrintHash("John")
PrintHash("Doe")
}
Java
package updm.hashing;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
/**
* Example of the hashing algorithm.
*
* <p>Sample hashes:
*
* <ul>
* <li>Email 'test@gmail.com': h5JGBrQTGorO7q6IaFMfu5cSqqB6XTp1aybOD11spnQ=
* <li>Phone '+18005551212': YdkRG+0+bZz8G8O1yzWkAmh8TxVGvuBhor1ET73WTEQ=
* <li>First name 'John': ltljLzY1ZMwwMlIUCc8iqFLyAy7sCZ7VlnwNAAzsYHo=
* <li>Last name 'Doe': eZ75KhGvkY4/t0HfQpNPO1aO0tk6wd908bjUGieTKm8=
* </ul>
*/
public final class HashExample {
private HashExample() {}
public static String hash(String token) {
// Normalizes and hashes the input token using SHA-256 and Base64 encoding.
String formattedToken = token.toLowerCase().strip();
byte[] hash;
try {
hash = MessageDigest.getInstance("SHA-256").digest(formattedToken.getBytes(UTF_8));
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("SHA-256 not supported", e);
}
return Base64.getEncoder().encodeToString(hash);
}
public static void printHash(String token) {
// Calculates and prints the hash for the given token.
System.out.printf("Hash: \"%s\"\t(Token: %s)\n", hash(token), token);
}
public static void main(String[] args) {
// Executes hash calculations and prints results for sample tokens.
printHash("test@gmail.com");
printHash("+18005551212");
printHash("John");
printHash("Doe");
}
}
SQL
/*
Provides the hashing algorithm, as well as some valid hashes of sample data for testing.
The following code uses Google Standard SQL and can be run on BigQuery to generate match tables from unhashed data.
Sample hashes:
- Email 'test@gmail.com': h5JGBrQTGorO7q6IaFMfu5cSqqB6XTp1aybOD11spnQ=
- Phone '+18005551212': YdkRG+0+bZz8G8O1yzWkAmh8TxVGvuBhor1ET73WTEQ=
- First name 'John': ltljLzY1ZMwwMlIUCc8iqFLyAy7sCZ7VlnwNAAzsYHo=
- Last name 'Doe': eZ75KhGvkY4/t0HfQpNPO1aO0tk6wd908bjUGieTKm8=
The unhashed input table schema is assumed to be:
- Column name: UserID, Type: String
- Column name: Email, Type: String
- Column name: Phone, Type: String
- Column name: FirstName, Type: String
- Column name: LastName, Type: String
- Column name: PostalCode, Type: String
- Column name: CountryCode, Type: String
*/
-- Creates a new table with Base64-encoded SHA-256 hashes of specified columns.
CREATE TABLE `your_project_name.your_dataset_name.output_hashed_table_name`
AS
SELECT
UserID,
TO_BASE64(SHA256(LOWER(Email))) AS Email,
TO_BASE64(SHA256(Phone)) AS Phone,
TO_BASE64(SHA256(LOWER(FirstName))) AS FirstName,
TO_BASE64(SHA256(LOWER(LastName))) AS LastName,
PostalCode,
CountryCode,
FROM
`your_project_name.your_dataset_name.input_unhashed_table_name`;
联接键
某些用户提供的数据组合比其他组合更有效。下面列出了不同用户提供的数据组合,按相对强度进行排名。如果您使用地址,则必须包含:名字、姓氏、国家/地区和邮政编码。
- 电子邮件地址、电话号码、地址(最强)
- 电话号码、地址
- 电子邮件地址、地址
- 电子邮件、电话
- 地址
- 电话
- 电子邮件(最弱)
创建匹配表
依次点击报告 > 创建报告 > 生成私有云匹配表 > 使用模板 可选:如果您的数据尚未经过哈希处理,您可以选择生成私有云匹配表(进行哈希处理)。
// Create a new match table using your first party data with this template. /* Parameters: Manually remove all the parameters tagged with @ prefix and replace them with column names from your first party table: * @user_id * @email * @phone * @first_name * @last_name * @country_code * @postal_code And your BigQuery table information: * @my_project: Your BigQuery project where the first party table is. * @my_dataset: Your dataset where the first party table is. * @my_first_party_table: Your first party table. */ CREATE OR REPLACE TABLE adh.updm_match_table AS ( SELECT CAST(@user_id AS BYTES) AS user_id, @email AS email, @phone AS phone, @first_name AS first_name, @last_name AS last_name, @country_code AS country, @postal_code AS zip_code FROM `@my_project.@my_dataset.@my_first_party_table` );
将参数名称替换为列名称,以提供适当的别名。
将隐私噪声设置切换为“使用差异检查”。
点击设置时间表,设置匹配表的刷新频率。每次运行都会覆盖当前的匹配表。
查询匹配的数据
查询匹配表
如果匹配表包含的数据足以满足隐私权检查要求,就可以针对该表运行查询。
第一方数据 (1PD) 的原始表由 my_data
表示。
这包括个人身份信息 (PII) 和非个人身份信息数据。与匹配表相比,使用原始表可以生成包含更多数据洞见的报告,因为它代表了范围内的所有第一方数据。
广告数据中心架构中包含 user_id
字段的每个表都附带一个匹配表。例如,对于 adh.google_ads_impressions
表,广告数据中心还会生成一个名为 adh.google_ads_impressions_updm
的匹配表,其中包含您的用户 ID。对于因政策而隔离的表,系统会创建单独的匹配表。例如,对于 adh.google_ads_impressions_policy_isolated_youtube
表,广告数据中心还会生成一个名为 adh.google_ads_impressions_policy_isolated_youtube_updm
的匹配表,其中包含您的用户 ID。
这些表都包含原始表中在 user_id
方面匹配的一部分用户。例如,如果原始表包含用户 A 和用户 B 的数据,但只有用户 A 是匹配项,那么用户 B 将不会出现在匹配表中。
匹配表包含一个额外的列,名为 customer_data_user_id
,该列会以字节形式存储用户标识符。
编写查询时,请务必考虑该字段的类型。SQL 比较运算符会认为您要比较的字面量属于同一类型。根据 user_id
在第一方数据表中的存储方式,您可能需要先对该表中的值进行编码,然后才能匹配数据。您需要将联接键转换为字节形式,才能实现成功匹配。
JOIN ON
adh.google_ads_impressions_updm.customer_data_user_id = CAST(my_data.user_id AS BYTES)
此外,在 SQL 中比较字符串时会区分大小写,因此您可能需要对您比较的两个字符串都进行编码,以确保比较结果准确。
查询示例
统计匹配的用户数
此查询会统计 Google Ads 展示次数表格中的匹配用户数。
/* Count matched users in Google Ads impressions table */
SELECT COUNT(DISTINCT user_id)
FROM adh.google_ads_impressions_updm
计算匹配率
并非所有用户都符合匹配条件。例如,系统不会通过 UPDM 匹配未登录的用户、儿童和未同意共享信息的用户。您可以使用 is_updm_eligible
字段来计算更准确的 UPDM 匹配率。请注意,is_updm_eligible
字段自 2024 年 10 月 1 日起可用。您无法使用此字段计算该日期之前的匹配率。
/* Calculate the UPDM match rate */
CREATE TEMP TABLE total_events OPTIONS(privacy_checked_export=TRUE) AS
SELECT
customer_id,
COUNT(*) AS n
FROM adh.google_ads_impressions
WHERE is_updm_eligible
GROUP BY 1;
CREATE TEMP TABLE matched_events OPTIONS(privacy_checked_export=TRUE) AS
SELECT
customer_id,
COUNT(*) AS n
FROM adh.google_ads_impressions_updm
GROUP BY 1;
SELECT
customer_id,
SAFE_DIVIDE(matched_events.n, total_events.n) AS match_rate
FROM total_events
LEFT JOIN matched_events
USING (customer_id)
联接第一方数据和 Google Ads 数据
此查询展示了如何将第一方数据与 Google Ads 数据联接:
/* Join first-party data with Google Ads data. The customer_data_user_id field
contains your ID as BYTES. You need to cast your join key into BYTES for
successful matches. */
SELECT
inventory_type,
COUNT(*) AS impressions
FROM
adh.yt_reserve_impressions_updm AS google_data_imp
LEFT JOIN
`my_data`
ON
google_data_imp.customer_data_user_id = CAST(my_data.user_id AS BYTES)
GROUP BY
inventory_type
UPDM 常见问题解答
如需查看与 UPDM 相关的常见问题解答列表,请参阅 UPDM 常见问题解答。