User-provided data matching (UPDM) joins first-party data that you've collected about a user—such as information from your websites, apps, or physical stores—with that same user's signed-in activity across all Google ad data, including Google owned & operated data. This includes data bought through Google Marketing Platform (GMP) products, for example, YouTube bought using Display & Video 360. Other GMP products which are not Google owned & operated are not supported.
To be eligible for user-provided data matching, the ad event must be linked to a signed-in user in Google ad data.
This document describes the user-provided data matching feature, and provides guidance on setup and use.
Overview
Gaining valuable advertising insights often requires stitching together data from multiple sources. Building your own solution to this data pipeline problem requires significant time investment and engineering investment. The Connections page in Ads Data Hub streamlines this process by providing a step-by-step, guided interface for importing, transforming, and matching data to BigQuery, so that you can use it in your Ads Data Hub queries, or any other product that reads from BigQuery. Enriching your queries with first-party data can deliver richer customer experiences, and is more resistant to industry-wide ad-tracking changes.
The Connections page is built with tools that allow you to encrypt and share personally identifiable information (PII) with partners in a privacy-centric manner. After selecting which columns contain PII, Ads Data Hub encrypts the data, ensuring that your first-party data can only be exported or read by people who have permission to do so. Knowing which first-party data is needed for your measurement or activation use case can be difficult, so Ads Data Hub provides a comprehensive list of predefined use cases, then guides you through the entire experience of extracting, transforming, and loading your data. While can create multiple types of connections, this document assumes that you are using the Connections page for user-provided data matching.
Supported first-party data sources
You can import data from these data sources:
- BigQuery
- Cloud Storage
- Secure FTP (sFTP)
- Snowflake
- MySQL
- PostgreSQL
- Amazon Redshift
- Amazon S3
Because user-provided data matching is only available on Google owned and operated inventory for signed-in users, it is not impacted by the upcoming deprecation of third-party cookies. Since it's more resistant to industry changes than third-party data, it can provide richer insights, which can lead to higher customer engagement.
Learn the terminology
- User-provided data connection: Set up a user-provided data connection to import and match your data, schedule data imports, transform data, and match your ads data using a user ID. The ad event must be linked to a signed-in user in Google ad data. Requires multiple Google Cloud projects.
- First-party data connection: Set up a first-party data connection as a data preparation tool, to schedule data imports and transform data without the advanced features of UPDM. This type of connection requires only one Google Cloud project.
- Data source: A connected product, imported file, or third-party integration–for example, BigQuery.
- Destination: A use case; typically a Google product or product feature, where imported data is activated–for example, Ads Data Hub user-provided data matching.
- Admin project: The Google Cloud project that contains your proprietary advertising data in its raw format.
- Output dataset: The BigQuery dataset that Ads Data Hub writes to. By default, this is a dataset under your admin project. To change it to another Google Cloud project, see Configure service accounts.
Process summary
- Setup of data ingestion and matching
- You grant the required permissions to the services accounts on your admin project. See Set up data ingestion.
- First-party data ingestion and matching
- You format and upload your first-party data to your BigQuery dataset. For the simplest setup, use your admin project. However, you can use any BigQuery dataset that you own.
- You initiate a data-matching request by creating a connection and setting an import schedule.
- Google joins data between your project and Google-owned data containing Google's user ID and hashed user-provided data to build and update match tables.
- See Ingest first-party data
- Ongoing queries in Ads Data Hub, based on matched data
- You run queries against the match tables in the same way you run regular queries in Ads Data Hub. See Query matched data.
Learn about privacy requirements
Collecting customer data
When using user-provided data matching, you must upload first-party data. This could be information you collected from your websites, apps, physical stores, or any information that a customer shared with you directly.
You must:
- Ensure that your privacy policy discloses that you share customer data with third parties to perform services on your behalf, and that you obtain consent for such sharing where legally required
- Only use Google's approved API or interface to upload customer data
- Comply with all applicable laws and regulations, including any self-regulatory or industry codes that may apply
First-party consent acknowledgement
To ensure you are able to use your first-party data in Ads Data Hub, you must confirm that you have obtained proper consent to share data from EEA end users with Google per the EU user consent policy and Ads Data Hub policy. This requirement applies to each Ads Data Hub account, and must be updated every time you upload new first-party data. Any one user can make this acknowledgement on behalf of the entire account.
Note that the same Google service query rules that apply to analysis queries also apply to UPDM queries. For example, you can't run cross-service queries on users in the EEA when you create a match table.
To learn how to acknowledge consent in Ads Data Hub, see Consent requirements for the European Economic Area.
Data size
To protect end-user privacy, user-provided data matching enforces these requirements regarding the size of your data:
- You must upload least 1,000 records in your user list.
- Every successful update of your match table must include a minimum number of newly matched users. This behavior is similar to difference checks.
- Your list must not exceed the maximum number of records. To learn about the maximum data limit, reach out to your Google representative.
Set up data ingestion
Before you start, you need to configure your Ads Data Hub account to create data connections, which is how you'll establish your data-matching pipeline. You only need to perform these steps once.
From the Connections page, click Begin setup to open the account setup wizard at the UPDM enablement stage.
What permissions are granted for BigQuery and Cloud Storage?
If you set up UPDM for use with BigQuery or Cloud Storage, use this reference to understand the permissions that are granted to the Ads Data Hub service accounts.
BigQuery
Datafusion service account | |||||||||
Purpose | The datafusion service account is used to display a list of source fields in the Ads Data Hub UI. | ||||||||
Format | service-some-number@gcp-sa-datafusion.iam.gserviceaccount.com |
||||||||
Required access |
|
||||||||
Dataproc service account | |||||||||
Purpose | The dataproc service account is responsible for running the data pipelines in the background. | ||||||||
Format | some-number-compute@developer.gserviceaccount.com |
||||||||
Required access |
|
||||||||
UPDM service account | |||||||||
Purpose | The UPDM service account is used to run the matching job. | ||||||||
Format | service-some-number@gcp-sa-adsdataconnector.iam.gserviceaccount.com |
||||||||
Required access |
|
Cloud Storage
Datafusion service account | |||||||
Purpose | The datafusion service account is used to display a list of source fields in the Ads Data Hub UI. | ||||||
Format | service-some-number@gcp-sa-datafusion.iam.gserviceaccount.com |
||||||
Required access |
|
||||||
Dataproc service account | |||||||
Purpose | The dataproc service account is responsible for running the data pipelines in the background. | ||||||
Format | some-number-compute@developer.gserviceaccount.com |
||||||
Required access |
|
||||||
UPDM service account | |||||||
Purpose | The UPDM service account is used to run the matching job. | ||||||
Format | service-some-number@gcp-sa-adsdataconnector.iam.gserviceaccount.com |
||||||
Required access |
|
Other data sources
Not necessary for other data sources
Ingest and match first-party data
Format data for input
Your data must adhere to these formatting requirements to be correctly matched:
- Where indicated in the following input field descriptions, you must upload using SHA256 hashing.
- Input fields must be formatted as strings. For example, if you're using
BigQuery's SHA256 hash
function
with the Base16 encoding function
(TO_HEX),
use the following transformation:
TO_HEX(SHA256(user_data))
. - UPDM supports both Base16 and Base64 encoding. You must align encoding of your first-party data with the decoding used in your Ads Data Hub query. If you change your first-party data encoding, you must update your Ads Data Hub query to decode from the same base. The following examples use Base16 encoding.
User ID
- Plain text
- Hashing: None
- Strip whitespace
- Lowercase all characters
- Include a domain name for all email addresses, such as gmail.com or hotmail.co.jp
- Remove accents—for example, change è, é, ê, or ë to e
- Hashing: Base16 encoded SHA256
Valid: TO_HEX(SHA256("jeffersonloveshiking@gmail.com"))
Invalid: TO_HEX(SHA256("JéffersonLôvesHiking@gmail.com"))
Phone
- Strip whitespace
- Format in E.164 format—for instance, US example: +14155552671, UK example: +442071838750
- Include country code (including US)
- Remove all special characters except the "+" before the country code
- Hashing: Base16 encoded SHA256
Valid: TO_HEX(SHA256("+18005550101"))
Invalid: TO_HEX(SHA256("(800) 555-0101"))
First name
- Strip whitespace
- Lowercase all characters
- Remove all prefixes, such as Mrs.
- Don't remove accents—for example, è, é, ê, or ë
- Hashing: Base16 encoded SHA256
Valid: TO_HEX(SHA256("daní"))
Invalid: TO_HEX(SHA256("Daní"))
Last name
- Strip whitespace
- Lowercase all characters
- Remove all prefixes, such as Jr.
- Don't remove accents—for example, è, é, ê, or ë
- Hashing: Base16 encoded SHA256
Valid: TO_HEX(SHA256("delacruz"))
Invalid: TO_HEX(SHA256("de la Cruz, Jr."))
Country
- Include the country code even if all of your customer data is from the same country
- Don't hash country data
- Use ISO 3166-1 alpha-2 country codes
- Hashing: None
Valid: US
Invalid: United States of America
or USA
Zip code
- Don't hash zip code data
- Both US and international zip and postal codes are allowed
- For US:
- 5 digit codes are allowed—for example, 94043
- 5 digits followed by 4 digit extension are also allowed—for example, 94043-1351 or 940431351
- For all other countries:
- No formatting needed (No need to lowercase, or remove spaces and special characters)
- Leave out postal code extensions
- Hashing: None
Hash validation and data encoding
You can use the following hash validation scripts to ensure that your data is correctly formatted.
JavaScript
Base16
/**
* @fileoverview Provides the hashing algorithm for User-Provided Data Match, as
* well as some valid hashes of sample data for testing.
*/
async function hash(token) {
const formattedToken = token.trim().toLowerCase();
const hashArrayBuffer = await crypto.subtle.digest(
'SHA-256', (new TextEncoder()).encode(formattedToken));
return Array.from(new Uint8Array(hashArrayBuffer))
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
}
function main() {
// Expected hash for test@gmail.com:
// 87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674
hash('test@gmail.com').then(result => console.log(result));
// Expected hash for +18005551212:
// 61d9111bed3e6d9cfc1bc3b5cb35a402687c4f1546bee061a2bd444fbdd64c44
hash('+18005551212').then(result => console.log(result));
// Expected hash for John:
// 96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a
hash('John').then(result => console.log(result));
// Expected hash for Doe:
// 799ef92a11af918e3fb741df42934f3b568ed2d93ac1df74f1b8d41a27932a6f
hash('Doe').then(result => console.log(result));
}
main()
Base64
/**
* @fileoverview Provides the hashing algorithm, as well as some valid hashes of
* sample data for testing.
*/
async function hash(token) {
const formattedToken = token.trim().toLowerCase();
const hashBuffer = await crypto.subtle.digest(
'SHA-256', (new TextEncoder()).encode(formattedToken));
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
Base16
"""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': 87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674
- Phone '+18005551212': 61d9111bed3e6d9cfc1bc3b5cb35a402687c4f1546bee061a2bd444fbdd64c44
- First name 'John': 96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a
- Last name 'Doe': 799ef92a11af918e3fb741df42934f3b568ed2d93ac1df74f1b8d41a27932a6f
"""
import base64
import hashlib
def updm_hash(token):
return hashlib.sha256(token.strip().lower().encode('utf-8')).hexdigest()
def print_updm_hash(token):
print('Hash: "{}"\t(Token: {})'.format(updm_hash(token), token))
def main():
print_updm_hash('test@gmail.com')
print_updm_hash('+18005551212')
print_updm_hash('John')
print_updm_hash('Doe')
if __name__ == '__main__':
main()
Base64
"""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):
return base64.b64encode(
hashlib.sha256(
token.strip().lower().encode('utf-8')).digest()).decode('utf-8')
def print_hash(token, expected=None):
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():
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
Base16
/*
Provides the hashing algorithm, as well as some valid hashes of sample data for testing.
Sample hashes:
- Email 'test@gmail.com': 87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674
- Phone '+18005551212': 61d9111bed3e6d9cfc1bc3b5cb35a402687c4f1546bee061a2bd444fbdd64c44
- First name 'John': 96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a
- Last name 'Doe': 799ef92a11af918e3fb741df42934f3b568ed2d93ac1df74f1b8d41a27932a6f
*/
package main
import (
"crypto/sha256"
"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 := fmt.Sprintf("%x", 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")
}
Base64
/*
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
Base16
package updm.hashing;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Ascii;
import com.google.common.hash.Hashing;
/**
* Example of the UPDM hashing algorithm using hex-encoded SHA-256.
*
* <p>This uses the Guava Hashing to generate the hash: https://github.com/google/guava
*
* <p>Sample valid hashes:
*
* <ul>
* <li>Email "test@gmail.com": "87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674"
* <li>Phone "+18005551212": "61d9111bed3e6d9cfc1bc3b5cb35a402687c4f1546bee061a2bd444fbdd64c44"
* <li>First name "John": "96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a"
* <li>Last name "Doe": "799ef92a11af918e3fb741df42934f3b568ed2d93ac1df74f1b8d41a27932a6f"
* </ul>
*/
public final class HashExample {
private HashExample() {}
public static String hash(String token) {
String formattedToken = Ascii.toLowerCase(token).strip();
return Hashing.sha256().hashString(formattedToken, UTF_8).toString();
}
public static void printHash(String token) {
System.out.printf("Hash: \"%s\"\t(Token: %s)\n", hash(token), token);
}
public static void main(String[] args) {
printHash("test@gmail.com");
printHash("+18005551212");
printHash("John");
printHash("Doe");
}
}
Base64
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) {
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) {
System.out.printf("Hash: \"%s\"\t(Token: %s)\n", hash(token), token);
}
public static void main(String[] args) {
printHash("test@gmail.com");
printHash("+18005551212");
printHash("John");
printHash("Doe");
}
}
SQL
Base16
/*
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': 87924606b4131a8aceeeae8868531fbb9712aaa07a5d3a756b26ce0f5d6ca674
- Phone '+18005551212': 61d9111bed3e6d9cfc1bc3b5cb35a402687c4f1546bee061a2bd444fbdd64c44
- First name 'John': 96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a
- Last name 'Doe': 799ef92a11af918e3fb741df42934f3b568ed2d93ac1df74f1b8d41a27932a6f
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
*/
CREATE TABLE `your_project_name.your_dataset_name.output_hashed_table_name`
AS
SELECT
UserID,
TO_HEX(SHA256(LOWER(Email))) AS Email,
TO_HEX(SHA256(Phone)) AS Phone,
TO_HEX(SHA256(LOWER(FirstName))) AS FirstName,
TO_HEX(SHA256(LOWER(LastName))) AS LastName,
PostalCode,
CountryCode,
FROM
`your_project_name.your_dataset_name.input_unhashed_table_name`;
Base64
/*
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
*/
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`;
Join keys
Some combinations of user-provided data are stronger than others. Following is a list of different user-provided data combinations, ranked by relative strength. If you use an address, you must include: First name, Last name, Country, and Zip code.
- Email, Phone, Address (strongest)
- Phone, Address
- Email, Address
- Email, Phone
- Address
- Phone
- Email (weakest)
Create a match table
- Click Connections > Create connection > User-provided data matching.
- Choose a data source, then click Connect.
- Authenticate, if prompted, then click Next:
BigQuery
Click Apply to grant access to BigQuery.
Cloud Storage
Click Apply to grant access to Cloud Storage.
MySQL
Enter your MySQL database location, port, username, and password.
S3
Enter your Amazon S3 secret access key.
PostgreSQL
Enter your PostgreSQL database location, port, username, password, and database.
Redshift
Enter your Redshift database location, port, username, password, and database.
sFTP
Enter your sFTP server location, username, and password.
Snowflake
Enter your Snowflake account identifier, username, and password.
- Configure your data source, then click Next:
BigQuery
Select the BigQuery table to import.
Cloud Storage
Enter the gsutil path, such as
gs://my-bucket/folder/
and select your file's formatting.If this is the first time you have connected this resource, an alert appears. Click Apply to grant access, then click Next. Note: you must have a role with permission to delegate
storage.buckets.setIamPolicy
for the relevant bucket.MySQL
Select the MySQL database and table that you want to use.
S3
Enter the URI to the file you want to upload, relative to the host address.
PostgreSQL
Enter the PostgreSQL schema and table (or view) name.
Redshift
Enter the Redshift schema and table (or view) name. By default, Redshift uses database location URLs that follow this template:
cluster-identifier.account-number.aws-region.redshift.amazonaws.com
.sFTP
Enter the file path and name, formatted as
/PATH/FILENAME.csv
Snowflake
Enter the Snowflake database, schema, and table (or view) that you want to use.
- Select a BigQuery dataset to use as an intermediary destination, then click Next. This step ensures that your data is correctly formatted.
- Optional: Modify the format of your data. Transformations include compute
hash, lower/upper case formatting, and merging/splitting fields.
- Click Action > > Transform.
- In the panel that pops up, click Add transformation or Add another transformation
- Choose a transformation type from the dropdown menu and enter the requirements.
- Click Save.
- Choose at least one join key and map the fields you will use. Ads Data Hub will automatically map fields with identical names, indicated by a . Make any necessary edits, then click Next.
- Set a schedule:
- Name your connection.
- Set a frequency, dictating how often data will be imported into the dataset that you selected in the previous step. Each run will overwrite data in the destination table.
- Specify how you'd like user ID collisions to be handled. You can choose between keeping the existing match or overwriting with new data.
- Click Finish. Match tables are generally ready to be queried 12 hours after they are created.
View connection details
The connection details page gives you information on a given connection's recent runs and errors. To view details for a specific connection:
- Click Connections.
- Click the name of the connection to view its details.
- You can now see the connection's details and recent runs. Each shows two
possible types of errors: connection-level (the connection didn't run) and
row-level errors (a row wasn't imported).
- A Failed status indicates that the entire connection failed to run (e.g. service account permission issue). Click on the error status to see which errors impacted the connection.
- A Completed status indicates that the connection ran successfully. However, there may still be row-level errors—indicated by a non-zero value in the "Rows with errors" column. Click the value to learn more about which records failed.
Edit a connection
The following details can be edited:
- Connection name
- Schedule
- Destination table
- Field mapping
Editing data source isn't supported. To change a data source, create a new connection and delete the old one.
To edit connection details:
- Click Connections.
- Click the name of the connection you want to edit.
- Edit the details you want to change:
- Connection name: Click Edit, enter the new name, then press Enter.
- Schedule: Click Edit, set the new schedule, then click Save.
- Destination table: Click Edit, enter the new destination name, then click Save.
- Field mapping: Click , make changes to fields, then click Save.
- Click .
Query matched data
Query the match tables
When your match tables contain enough data to satisfy privacy checks, you're ready to run queries against the tables.
The original table for first-party data (1PD) is represented by my_data
.
This includes both Personally Identifiable Information (PII) and non-PII data.
Using the original table can improve your reports with more insights, as it
represents all the 1PD data in scope, when compared to a match table.
Each table in the Ads Data Hub schema containing a user_id
field is
accompanied by a match table. For example, for the
adh.google_ads_impressions
table, Ads Data Hub also generates a match table
called adh.google_ads_impressions_updm
containing your user IDs.
Separate match tables are created for policy-isolated tables. For example, for
the adh.google_ads_impressions_policy_isolated_youtube
table, Ads Data Hub
also generates a match table called
adh.google_ads_impressions_policy_isolated_youtube_updm
containing
your user IDs.
These tables contain a subset of the users available in the original tables,
where there is a match on the user_id
. For example, if the original table
contains data for User A and User B, but only User A is matched, then User B
won't be in the match table.
The match tables contain an additional column called customer_data_user_id
, which
stores the user identifier as BYTES.
It's important to consider the field's type when writing your queries. SQL
comparison operators expect that the literals you're comparing are of the same
type. Depending on how the user_id
is stored in your table of first-party
data, you may need to encode the values in the table before matching the data.
You need to cast your join key into BYTES for successful matches:
JOIN ON
adh.google_ads_impressions_updm.customer_data_user_id = CAST(my_data.user_id AS BYTES)
Additionally, string comparisons in SQL are sensitive to capitalization, so you may need to encode strings on both sides of your comparison to ensure that they can be accurately compared.
Sample queries
Count matched users
This query counts the number of matched users in your Google Ads impressions table.
/* Count matched users in Google Ads impressions table */
SELECT COUNT(DISTINCT user_id)
FROM adh.google_ads_impressions_updm
This query shows how to join first-party data with Google Ads data:
/* 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 match rate FAQs
For a list of FAQs related to the UPDM match rate, see UPDM match rate FAQs.