18.5 Understand the Effect of Column Security on Analytical Results
When an end user lacks authorization to view a specific column value,
column-level security in Oracle Deep Data Security (Deep Sec) replaces that value
with NULL instead of filtering out the entire row.
Because Oracle SQL applies standard
NULL-handling rules to these column values, the
outputs of counts, groupings, sort orders, window functions, and
statistical calculations can be significantly altered, even though
the total row count remains unchanged.
The following sections explain how specific Oracle SQL functions process these
NULL values.
18.5.1 About Core SQL Behavior for Masked Data
When an unauthorized value is returned as NULL, standard
SQL conditions (such as IS NULL or equality checks) cannot distinguish it
from a genuine NULL stored in the database. If your query must
differentiate between the two, use the ORA_IS_COLUMN_AUTHORIZED function on
the base column.
- A comparison such as
sensitive_column = 'X'evaluates toUNKNOWNwhensensitive_columnisNULL; therefore, the row is not selected. - The
sensitive_column IS NULLpredicate evaluates toTRUEfor both storedNULLvalues and unauthorized values that have been masked toNULL. - Most aggregate functions ignore
NULLvalues. However,COUNT(*)counts every row, regardless of whether a column value isNULL. - If no non-
NULLvalues remain,COUNT(expression)returns0, whereasSUM,AVG,MIN,MAX, and other aggregates that ignoreNULLvalues returnNULL. - Scalar expressions can preserve or replace
NULLvalues. For example,NVL(sensitive_column, 0)replacesNULLwith0for use in subsequent calculations.
18.5.2 About Analytical Function-Specific Effects
Each SQL feature applies its normal NULL behavior to the
rows and columns visible to the query. The following tables describe how specific
analytical operations react to masked values and provide design guidance for
handling them.
Core SQL and NULL behavior
| SQL Feature | Effect on Masked (NULL) Values | Design Guidance |
|---|---|---|
|
Projection and scalar expressions |
The expression receives
|
Substitute a display value only in
the final |
|
|
Equality, inequality, and range
comparisons with |
Use
|
|
|
Counts a returned row even if one or more of its column values are unauthorized. |
If a row should not be counted at all, restrict access with the data grant's row predicate. |
|
|
These functions ignore unauthorized
values returned as |
Display |
Windowing, ordering, and ranking
| SQL Feature | Effect on Masked (NULL) Values | Design Guidance |
|---|---|---|
|
|
Default |
Do not assume that a skipped value means the row was entirely removed from the data set. |
|
|
|
Explicitly specify |
|
|
Rows whose partitioning or
grouping value is |
Ensure that a partitioning or grouping column is available when the query must keep its values separate. |
|
|
A row remains in a |
Distinguish between the number of rows in the
frame versus the number of
non- |
|
|
Rows whose ordering value is
|
Use ordering columns that the end user is authorized to access. When the query requires a stable order, add an authorized column that makes the ordering unique. |
|
|
An unauthorized ranking value is
|
Ensure both the ranking column and the aggregate column are authorized when the result depends on them. |
Cross-tabulation, joins, grouping, and result shaping
| SQL Feature | Effect on Masked (NULL) Values | Design Guidance |
|---|---|---|
|
|
A |
Restrict rows with the data grant's row predicate when necessary, and ensure that the pivot key, measure, and required grouping columns are available. |
|
|
For a single-measure
|
If the result must show why a
value is |
|
Joins |
An unauthorized join value is
|
Ensure that columns used for analytical joins are available, or use data grant row predicates to limit the rows that can take part in the join. |
|
Partitioned outer joins |
Data densification can create rows
and |
Keep the densification key in the
query so that a |
|
|
Rows with an unauthorized grouping
value are grouped under |
Use
|
|
|
Unauthorized ordering values are
|
Use partitioning and ordering columns that the end user is authorized to access. When the result requires a stable order, add an authorized column that makes the ordering unique. |
Statistical, approximate, and numerical analysis
| SQL Feature | Effect on Masked (NULL) Values | Design Guidance |
|---|---|---|
|
|
The function ignores unauthorized
values returned as |
Do not interpret this estimate as the total number of rows returned by the query. |
|
|
A regression pair is not used when
either the dependent or independent expression is |
Compare
|
|
|
|
Check the |
UTL_NLA |
An unauthorized matrix or vector
element is |
Do not call the routine with incomplete input, or ensure that every required element is available. |
Pattern and model analysis
| SQL Feature | Effect on Masked (NULL) Values | Design Guidance |
|---|---|---|
|
|
A comparison in
|
Ensure that columns used by
|
|
|
An unauthorized measure is a
|
Ensure that required model
dimensions and measures are available, and write rules for the resulting
|
18.5.3 Interpret PIVOT
Results
The PIVOT operation processes only the pivot keys and
aggregate inputs that are visible to the query. A NULL pivot key
does not match a listed non-NULL value, and a
NULL-ignoring aggregate does not use a NULL
measure.
In the sales_app.tenant_sales_analytics data grant
example, the secret_note column is not included in
the data grant’s column list, so its values are returned as
NULL.
secret_note as both the
aggregate input and the pivot key while attempting to count a
specific note
value:SELECT NVL(a1, 0) AS note_count
FROM (
SELECT secret_note
FROM sales_app.sales
)
PIVOT (
COUNT(secret_note)
FOR secret_note IN ('TEN10-A-1' AS a1)
);
NOTE_COUNT
---------
0The query returns 0 because secret_note is masked
as NULL and therefore does not match
'TEN10-A-1'. Consequently,
COUNT(secret_note) has no
non-NULL values to count.
secret_note is returned as
NULL. A query that uses
COUNT(*) can still count those rows because
COUNT(*) does not evaluate the contents of
the secret_note column.
Tip:
PIVOT implicitly groups by any
source columns not referenced in the pivot clause. Select
only the source columns strictly required by the operation,
and ensure the end user is authorized to access every pivot
or grouping column the query uses.
18.5.4 Interpret Regression Results
Regression aggregate functions, such as REGR_COUNT and
REGR_SLOPE, form a pair from the dependent and independent expressions
in each row. If either expression evaluates to NULL, the pair is discarded
and not used in the calculation.
The REGR_COUNT function is useful for showing how many
pairs contributed to the result.
secret_note and
month_no:SELECT REGR_COUNT(LENGTH(secret_note), month_no) AS sample_count
FROM sales_app.sales;SAMPLE_COUNT
---------
0If secret_note is restricted by a data grant, its value
is returned as NULL in every row. Consequently, no single row
supplies both of the values required to form a valid regression pair.
18.5.5 Interpret
APPROX_COUNT_DISTINCT Results
The APPROX_COUNT_DISTINCT function estimates the number of
distinct non-NULL values in the rows returned by the query.
Rows excluded by data grants do not contribute at all, and unauthorized
column values returned as NULL are not counted.
SELECT category,
COUNT(DISTINCT customer_id) AS exact_customers,
APPROX_COUNT_DISTINCT(customer_id) AS approximate_customers
FROM sales_app.sales
GROUP BY category
ORDER BY category;
CATEGORY EXACT_CUSTOMERS APPROXIMATE_CUSTOMERS
-------- --------------- ---------------------
A 3 3
B 1 1For this small data set, both calculations
correctly return 3 for category A
and 1 for category B. The
approximate result remains an estimate. If there are
other rows in the table belonging to restricted
tenants, their customer identifiers do not
contribute to these totals because those rows are
entirely filtered out by the data grant before the
aggregation occurs.