CertSafari
    Databricks Certified Data Engineer Professional - October 9 onwards· Lessons

    Domain 6 · Lesson 35/46

    Suppression, generalization and hashing with column masks

    Apply anonymization and pseudonymization methods — such as hashing, tokenization, suppression, and generalization — to confidential data (e.g., using column masks and related Unity Catalog features).

    10 min read
    1.6% of exam
    3 sources
    Published 2 Oct 2026
    Docs as of 30 Sep 2026

    What you will be able to do

    • Implement suppression with a mask that returns a constant
    • Implement generalization by rounding values or revealing only part of them
    • Implement deterministic pseudonymization with SHA2 and a version parameter for rotation
    • Use USING COLUMNS for conditional masking, and apply one method across many tables with a tag-driven ABAC policy

    1.Suppression: replace the value outright

    A Unity Catalog column mask is a SQL UDF bound to a column. It is evaluated at query time and returns either the real value or a protected one. Each anonymization method is a different body for that function. The bluntest is suppression: the protected branch returns a constant, so nothing about the original value is visible. The SSN mask returning '***-**-****' and the address mask returning 'REDACTED' both work this way. So does this one-line PII mask from the ABAC pattern library.

    A suppression mask: every value becomes the same constantsql
    CREATE FUNCTION catalog.schema.mask_pii(val STRING)
    RETURNS STRING
    RETURN '***';

    The constant has to fit the column. A STRING column can return '***'. A DOUBLE column has to suppress with a number such as 0.0, because the return value must be the column's type or castable to it.

    Checkpoint 1 of 6· Check yourself

    You need to suppress a DOUBLE salary column for non-HR users. Which ELSE branch is valid?

    Sources1

    2.Generalization: keep the shape, lose the precision

    Generalization keeps some analytic value. Instead of hiding the value completely, the mask makes it coarser. In the salary mask below, admins and HR see exact figures, managers see salaries rounded to the nearest thousand, and everyone else sees 0.0. One function therefore gives three audiences full access, generalized values and suppressed values.

    Checkpoint 2 of 6· Fill the gap

    Which function produces the generalized, rounded salary for managers?

    -- Succeeds: Masks a DOUBLE column, returns DOUBLE in every branch
    CREATE FUNCTION mask_salary(salary DOUBLE, user_role STRING)
    RETURNS DOUBLE
    RETURN CASE
      WHEN user_role IN ('admin', 'hr') THEN salary
      WHEN user_role = 'manager' THEN  ? (salary / 1000) * 1000
      ELSE 0.0
    END;

    Partial reveal is the string version of generalization: show the last few characters and hide the rest. Databricks recommends string functions for this rather than regular expressions, because regex masking scans the whole value on every row.

    Partial reveal with string operations, not regexsql
    CREATE FUNCTION mask_ssn(ssn STRING, show_last INT) RETURNS STRING
    DETERMINISTIC
      RETURN CONCAT('***-**-', RIGHT(ssn, show_last));

    Sources1

    3.Hashing: deterministic pseudonymization

    Suppression and generalization throw information away. Hashing keeps identity but hides the real value: the same input always produces the same pseudonym, so joins, counts of distinct customers and grouping still work. The documented pattern adds a version number to the value before hashing. Changing the version rotates the pseudonyms, while data hashed under the earlier version keeps its existing hashes. The DETERMINISTIC keyword tells the engine the output depends only on the inputs, which helps it optimize the query.

    Versioned consistent hashing with SHA2sql
    CREATE FUNCTION pseudonymize(val STRING, version INT) RETURNS STRING
    DETERMINISTIC
      RETURN SHA2(CONCAT(val, CAST(version AS STRING)), 256);

    Checkpoint 3 of 6· Check yourself

    Your security team wants all pseudonyms produced by pseudonymize() to change from now on. What does the documented pattern suggest?

    Checkpoint 4 of 6· Exam question

    An analytics team is only permitted to study workforce age distribution in bands (e.g. "born in the 1990s") and must never see an employee's exact `date_of_birth`, while HR staff still need the full date for benefits administration. The engineer must implement this with a single Unity Catalog column mask on `date_of_birth`. Which masking approach satisfies both requirements?

    Sources1

    4.Conditional masking and applying a method at scale

    The USING COLUMNS clause passes extra arguments to a mask. The first parameter is always the masked column. Further parameters can be other columns in the same row or constants. This is how the rotation version reaches pseudonymize, and it also lets one mask treat rows differently. In the example below, a user sees an address only if they belong to the group for that row's country.

    A mask that reads a second column through USING COLUMNSsql
    CREATE FUNCTION mask_address_by_country(address STRING, country STRING, group_suffix STRING DEFAULT '_address_viewers')
    RETURN IF(
      is_account_group_member(country || group_suffix),
      address,
      'REDACTED'
    );

    Binding a mask to each column with ALTER TABLE stops being practical once PII is spread across many tables. Databricks recommends ABAC policies for this. A policy is attached to a catalog or schema and applies the same masking function to every column whose governed tag matches. The policy below applies the mask_pii suppression function to any column tagged as a name or address, once the table is no longer tagged as unverified.

    An ABAC policy applying a suppression mask to every PII-tagged column in a catalogsql
    CREATE POLICY mask_reviewed_pii
    ON CATALOG my_catalog
    COLUMN MASK catalog.schema.mask_pii
    TO `account users`
    EXCEPT `data_admins`
    FOR TABLES
    WHEN NOT has_tag_value('classification', 'unverified')
    MATCH COLUMNS (has_tag_value('pii', 'name') OR has_tag_value('pii', 'address')) AS m
    ON COLUMN m;

    ABAC policies also allow one UDF that takes and returns VARIANT to mask columns of many types. That casting works only inside ABAC policies, not in table-level masks. Whichever mechanism you use, keep the number of masks down: mask only the columns that really are sensitive, and reuse functions.

    Anonymization methods as column-mask function bodies
    MethodDocumented exampleWhat survives
    Suppressionmask_pii returns '***'Nothing about the value
    Generalizationmask_salary: ROUND(salary / 1000) * 1000Approximate magnitude
    Partial revealmask_ssn: CONCAT('*--', RIGHT(ssn, show_last))The last characters
    Hashing (pseudonymization)pseudonymize: SHA2(CONCAT(val, CAST(version AS STRING)), 256)Equality across tables, for joins

    Checkpoint 5 of 6· Match them up

    Match each requirement to the technique that meets it

    Tap a term, then the definition that fits it.

    Checkpoint 6 of 6· Exam question

    For PCI compliance, a payments table stores full credit card numbers in `card_number`. Fraud investigators must be able to recover the original number for flagged transactions, while all other analysts should see only a surrogate value. The engineer implements this as a Unity Catalog column mask whose function queries a separate `card_token_vault` table keyed on the original number. Which statements correctly describe requirements for this tokenization design? (Select all that apply)(Select 3)

    Sources231

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.A suppression mask can return a text placeholder such as 'CONFIDENTIAL' for any column.Why is that wrong?

      Every branch must return the column's type or a type castable to it. For a DOUBLE column, suppress with a number such as 0.0.

      Covered in Suppression: replace the value outright

    2. 2.A generic VARIANT masking function works as a table-level mask set with ALTER TABLE.Why is that wrong?

      The automatic VARIANT casting for complex types works only inside ABAC policies.

      Covered in Conditional masking and applying a method at scale

    Practise it for real

    Bind a suppression mask to an SSN column, confirm that users outside the HR group see it masked, then remove it safely

    1. 1.Run CREATE FUNCTION ssn_mask(ssn STRING) RETURN CASE WHEN is_account_group_member('HumanResourceDept') THEN ssn ELSE '*--****' END;

      Why: The mask must exist as a SQL UDF in Unity Catalog before it can be bound

      You should see: The function is created in your current schema

    2. 2.Run CREATE TABLE users (name STRING, ssn STRING); then ALTER TABLE users ALTER COLUMN ssn SET MASK ssn_mask;

      Why: This binds the mask to an existing column

      You should see: The ssn column shows a column mask in Catalog Explorer

    3. 3.Insert a row, then run SELECT * FROM users as a user who is not in HumanResourceDept

      Why: The mask is evaluated per query against the caller's group membership

      You should see: ssn returns *--****

    4. 4.Run ALTER TABLE users ALTER COLUMN ssn DROP MASK; and only then DROP FUNCTION ssn_mask;

      Why: Dropping the function first would leave the table inaccessible

      You should see: Queries return original ssn values and the table remains readable

    Stuck? Get a nudge

    If you are in HumanResourceDept you will see real values. Test with another user, or temporarily change the group name in the function.

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “When masking a column, return the same data type or a type that is castable to it.”
      ↩︎ Suppression: replace the value outright
      “Reveal part of a sensitive value using string operations instead of regex.”
      ↩︎ Generalization: keep the shape, lose the precision
      “Regex-based masking scans the entire value for every row, which is expensive on large text fields”
      ↩︎ Generalization: keep the shape, lose the precision
      “Consistent hashing (also called deterministic pseudonymization) replaces sensitive data with a hashed value that is the same across multiple tables.”
      ↩︎ Hashing: deterministic pseudonymization
      “Marking a function as DETERMINISTIC tells the engine that the function always returns the same result for the same input”
      ↩︎ Hashing: deterministic pseudonymization
      “A single masking UDF that accepts and returns a VARIANT can mask columns of many data types”
      ↩︎ Conditional masking and applying a method at scale
      “When masking a column, return the same data type or a type that is castable to it.”
      ↩︎ Exam trap 1
      “This casting works only within ABAC column mask and row filter policies, not in general SQL or table-level masks.”
      ↩︎ Exam trap 2
      “'CONFIDENTIAL' cannot be cast to a DOUBLE column type”
      ↩︎ Checkpoint
      “Increment the version number through the policy's USING COLUMNS clause to generate new hashes without breaking historical data that used the previous version.”
      ↩︎ Checkpoint
    2. 2.
      “The first parameter of the masking function always maps to the masked column itself.”
      ↩︎ Conditional masking and applying a method at scale
      “USING COLUMNS enables conditional masking based on values beyond the column being masked.”
      ↩︎ Checkpoint
    3. 3.
      “ABAC policies attach at the catalog or schema level and apply automatically based on governed tags, rather than requiring per-table configuration.”
      ↩︎ Conditional masking and applying a method at scale
      “Apply masks only to truly sensitive columns and reuse masking functions where possible.”
      ↩︎ Conditional masking and applying a method at scale

    Spotted a mistake, or was something unclear? Tell us.