Zugriffsverlauf¶
Unter diesem Thema werden Konzepte zum Benutzerzugriffsverlauf in Snowflake bereitgestellt.
Unter diesem Thema:
Übersicht¶
Der Zugriffsverlauf in Snowflake bezieht sich darauf, wann die Benutzerabfrage Daten liest und wann die SQL-Anweisung eine Datenschreiboperation wie INSERT, UPDATE und DELETE sowie Variationen des COPY-Befehls vom Quelldatenobjekt zum Zieldatenobjekt ausführt. Der Benutzerzugriffsverlauf kann durch Abfrage der Account Usage-Ansicht ACCESS_HISTORY abgerufen werden. Mithilfe der Datensätze in dieser Ansicht kann die Einhaltung gesetzlicher Bestimmungen einfacher geprüft werden, und sie bieten Einblick in beliebte und häufig abgefragte Tabellen und Spalten, denn es gibt eine direkte Verbindung zwischen dem Benutzer (d. h. Abfrageoperator), der Abfrage, der Tabelle bzw. Ansicht, der Spalte und den Daten.
Jede Zeile in der ACCESS_HISTORY-Ansicht enthält einen einzelnen Datensatz pro SQL-Anweisung. Der Datensatz enthält Informationen zu den Quellspalten, auf die die Abfrage direkt und indirekt zugegriffen hat (d. h. die zugrunde liegenden Tabellen, aus denen die Daten für die Abfrage stammen) und die projizierten Spalten, die der Benutzer im Abfrageergebnis sieht. Wenn die Abfrage nicht auf eine Spalte im Abfrageergebnis zugreift oder diese projiziert, wird diese Spalte im Datensatz der Ansicht nicht erfasst, da die Daten dieser Spalte dem Benutzer nicht zur Verfügung gestellt werden. Beispiel:
CREATE OR REPLACE VIEW v1 (vc1, vc2) AS
SELECT c1 as vc1,
c2 as vc2
FROM t
WHERE t.c3 > 0
;
Die Tabellenspalten C1 und C2 sind Quellspalten, auf die die Ansicht direkt zugreift und die in der Spalte BASE_OBJECTS_ACCESSED der ACCESS_HISTORY-Ansicht erfasst sind.
Die Ansichtsspalten VC1 und VC2 sind projizierte Spalten, die der Benutzer bei der Abfrage der Ansicht sieht und die in der Spalte DIRECT_OBJECTS_ACCESSED der ACCESS_HISTORY-Ansicht erfasst werden.
Die Tabellenspalte C3 wird verwendet, um die Zeilen zu filtern, die die Ansicht enthält. Diese Spalte wird nicht in der ACCESS_HISTORY-Ansicht erfasst.
Eine Abfrageansicht, z. B.
SELECT * FROM v1;
, erfasst VC1 und VC2 als die Spalten, auf die zugegriffen wurde.
Bemerkung
Datensätze in der Account Usage-Ansicht QUERY_HISTORY werden nicht immer in der Ansicht ACCESS_HISTORY erfasst. Die Struktur der SQL-Anweisung bestimmt, ob Snowflake Datensätze in der ACCESS_HISTORY-Ansicht erfasst.
Einzelheiten zu den Lese- und Schreiboperationen, die Snowflake in der ACCESS_HISTORY-Ansicht unterstützt, finden Sie unter Nutzungshinweise.
Verfolgen von Lese- und Schreiboperationen¶
Die ACCESS_HISTORY-Ansicht enthält folgende Spalten:
QUERY_ID | QUERY_START_TIME | USER_NAME | DIRECT_OBJECTS_ACCESSED | BASE_OBJECTS_ACCESSED | OBJECTS_MODIFIED | OBJECT_MODIFIED_BY_DDL | POLICIES_REFERENCED
Leseoperationen werden über die ersten fünf Spalten verfolgt, während die letzte Spalte OBJECTS_MODIFIED die Datenschreibinformationen angibt, die Snowflake-Spalten, -Tabellen und -Stagingbereiche betreffen.
Die Abfrage in Snowflake und die Methode zum Erstellen von Datenbankobjekten bestimmen die Informationen, die Snowflake in den Spalten DIRECT_OBJECTS_ACCESSED, BASE_OBJECTS_ACCESSED und OBJECTS_MODIFIED zurückgibt.
Gleiches gilt, wenn die Abfrage auf ein Objekt verweist, das durch eine Zeilenzugriffsrichtlinie geschützt ist, oder auf eine Spalte, die durch eine Maskierungsrichtlinie geschützt ist, erfasst Snowflake die Richtlinieninformationen in der Spalte POLICIES_REFERENCED.
In der Spalte OBJECT_MODIFIED_BY_DDL wird die auf einer Datenbank, einem Schema, einer Tabelle, einer Ansicht oder einer Spalte ausgeführte DDL-Operation erfasst. Zu diesen Operationen gehören auch Anweisungen, die eine Zeilenzugriffsrichtlinie auf einer Tabelle oder Ansicht, eine Maskierungsrichtlinie auf einer Spalte sowie Tag-Aktualisierungen (z. B. Setzen eines Tags, Ändern eines Tag-Werts) auf dem Objekt oder der Spalte angeben.
Weitere Informationen zu den Spalten finden Sie im Abschnitt Spalten zur ACCESS_HISTORY-Ansicht.
Lesen¶
Betrachten Sie das folgende Szenario, um ein besseres Verständnis der Leseabfrage und der Erfassung dieser Informationen in der ACCESS_HISTORY-Ansicht zu erhalten:
Eine Reihe von Objekten:
base_table
»view_1
»view_2
»view_3
.Eine Leseabfrage auf
view_2
, wie z. B.:select * from view_2;
In diesem Beispiel gibt Snowflake Folgendes zurück:
view_2
in der Spalte DIRECT_OBJECTS_ACCESSED, weil in der Abfrageview_2
angegeben ist.base_table
in der Spalte BASE_OBJECTS_ACCESSED, da dies die ursprüngliche Quelle der Daten inview_2
ist.
Beachten Sie, dass view_1
und view_3
nicht in den Spalten DIRECT_OBJECTS_ACCESSED und BASE_OBJECTS_ACCESSED enthalten sind, da keine dieser Ansichten in der Abfrage enthalten war und sie nicht das Basisobjekt sind, das als Quelle für die Daten in view_2
dient.
Schreiben¶
Betrachten Sie das folgende Szenario, um ein besseres Verständnis der Schreiboperation und der Erfassung dieser Informationen in der ACCESS_HISTORY-Ansicht zu erhalten:
Eine Datenquelle:
base_table
Erstellen Sie eine Tabelle aus der Datenquelle (d. h. CTAS):
create table table_1 as select * from base_table;
In diesem Beispiel gibt Snowflake Folgendes zurück:
base_table
in den Spalten BASE_OBJECTS_ACCESSED und DIRECT_OBJECTS_ACCESSED, da auf die Tabelle direkt zugegriffen wurde und sie die Quelle der Daten ist.table_1
in der Spalte OBJECTS_MODIFIED mit den Spalten, in die beim Erstellen der Tabelle geschrieben wurde.
Spaltenherkunft¶
Spaltenherkunft (d. h. der Zugriffsverlauf von Spalten) erweitert die Account Usage-Ansicht ACCESS_HISTORY umd die Angabe, wie die Daten bei einer Schreiboperation von der Quellspalte zur Zielspalte gelangen. Snowflake verfolgt die Daten aus den Quellspalten über alle nachfolgenden Tabellenobjekte, die auf Daten aus den Quellspalten verweisen (z. B. INSERT, MERGE, CTAS), vorausgesetzt, dass Objekte in der Herkunftsabfolge nicht gelöscht werden. Snowflake stellt die Spaltenherkunft bereit, indem die ACCESS_HISTORY-Ansicht um die Spalte OBJECTS_MODIFIED erweitert wird.
Weitere Informationen dazu finden Sie unter:
Vorteile (unter diesem Thema)
Beispiel: Spaltenherkunft (unter diesem Thema)
Richtlinienreferenzen von Maskierungs- und Zeilenzugriffsrichtlinien¶
Die Spalte POLICY_REFERENCED gibt das Objekt an, das eine Zeilenzugriffsrichtlinie für eine Tabelle bzw. eine Maskierungsrichtlinie für eine Spalte hat, einschließlich aller Zwischenobjekte, die entweder durch eine Zeilenzugriffsrichtlinie oder eine Maskierungsrichtlinie geschützt sind. Snowflake erfasst die Richtlinie, die für die Tabelle oder Spalte erzwungen wird.
Betrachten Sie diese Objekte:
t1
» v1
» v2
Wobei:
t1
ist eine Basistabelle.v1
ist eine aus der Basistabelle erstellte Ansicht.v2
ist eine ausv1
erstellte Ansicht.
Wenn der Benutzer v2
abfragt, wird in der Spalte POLICIES_REFERENCED entweder die Zeilenzugriffsrichtlinie, die v2
schützt, oder jede Maskierungsrichtlinie, die die Spalten in v2
schützt, oder es werden ggf. beide Arten von Richtlinien erfasst. Außerdem werden in dieser Spalte alle Maskierungs- oder Zeilenzugriffsrichtlinien erfasst, die t1
und v1
schützen.
Anhand dieser Datensätze können Datenverwalter nachvollziehen, wie auf ihre richtliniengeschützten Objekte zugegriffen wird.
Die Spalte POLICIES_REFERENCED bietet weitere Vorteile für die ACCESS_HISTORY-Ansicht:
Identifizieren der richtliniengeschützten Objekte, auf die ein Benutzer in einer bestimmten Abfrage zugreift.
Vereinfachen des Prüfprozesses (Auditing) von Richtlinien.
Durch Abfragen der ACCESS_HISTORY-Ansicht sind keine komplexen Verknüpfungen mit anderen Account Usage-Ansichten (z. B. POLICY_REFERENCES und QUERY_HISTORY) mehr erforderlich, um Informationen zu den geschützten Objekten und den geschützten Spalten zu erhalten, auf die ein Benutzer Zugriff hat.
Unterstützte Operationen¶
Eine vollständige Beschreibung der von der ACCESS_HISTORY-Ansicht unterstützten Lese- und Schreiboperationen finden Sie im Abschnitt „Nutzungshinweise“ unter ACCESS_HISTORY-Ansicht.
Vorteile¶
Der Zugriffsverlauf in Snowflake bietet die folgenden Vorteile in Bezug auf Lese- und Schreiboperationen:
- Datenerkennung
Erkennen von nicht verwendeten Daten, um zu entscheiden, ob die Daten archiviert oder gelöscht werden sollen.
- Verfolgen der Bewegung sensibler Daten
Verfolgen Sie Datenbewegungen von einem externen Cloudspeicherort (z. B. Amazon S3-Bucket) zur Snowflake-Zieltabelle und umgekehrt.
Verfolgen Sie interne Datenbewegungen von einer Snowflake-Tabelle zu einer anderen Snowflake-Tabelle.
Nachdem Sie die Bewegung sensibler Daten verfolgt haben, wenden Sie Richtlinien (Maskierung und Zeilenzugriff) an, um die Daten zu schützen, aktualisieren Sie die Zugriffssteuerungseinstellungen, um den Zugriff auf Stagingbereich und Tabelle weiter zu regulieren, und setzen Sie Tags, um sicherzustellen, dass Stagingbereiche, Tabellen und Spalten mit sensiblen Daten für Compliance-Anforderungen nachverfolgt werden können.
- Datenvalidierung
Die Genauigkeit und Integrität von Berichten, Dashboards und Datenvisualisierungsprodukten wie Diagrammen und Grafiken werden validiert, da die Daten bis zu ihrer ursprünglichen Quelle zurückverfolgt werden können.
Datenverwalter können Benutzer benachrichtigen, bevor eine bestimmte Tabelle oder Ansicht gelöscht oder geändert wird.
- Compliance-Prüfung
Identifizieren Sie den Snowflake-Benutzer, der eine Schreiboperation auf einer Tabelle oder einem Stagingbereich ausgeführt hat, und wann diese Schreiboperation stattgefunden hat, um Compliance-Anforderungen wie GDPR oder CCPA zu erfüllen.
- Verbesserung der allgemeinen Data Governance
Die ACCESS_HISTORY-Ansicht bietet einen Gesamtüberblick, auf welche Daten zugegriffen wurde, wann der Datenzugriff stattfand und wie die Daten, auf die zugegriffen wurde, vom Datenquellenobjekt zum Datenzielobjekt verschoben wurden.
Die Spaltenherkunft bietet folgende weitere Vorteile:
- Abgeleitete Objekte schützen
Datenverwalter können auf Quellspalten mit sensiblen Daten auf einfache Weise Tags setzen, ohne dass nach dem Erstellen abgeleiteter Objekte (z. B. CTAS) zusätzlicher Aufwand erforderlich ist. Anschließend kann der Datenverwalter Tabellen, die sensible Spalten enthalten, mit einer Zeilenzugriffsrichtlinie schützen oder die sensiblen Spalten selbst entweder mit einer Maskierungsrichtlinie oder einer Tag-basierten Maskierungsrichtlinie schützen.
- Kopierhäufigkeit von Spalten mit sensiblen Daten
Datenschutzbeauftragte können schnell die Objektanzahl (z. B. 1 Tabelle, 2 Ansichten) einer Spalte ermitteln, die sensible Daten enthält. Mit dem Wissen, wie oft eine Spalte mit sensiblen Daten in einem Tabellenobjekt vorkommt, können die Datenschutzbeauftragten nachweisen, wie Standards für die Einhaltung von Vorschriften, wie z. B. der Datenschutz-Grundverordnung (DGVO) in der Europäischen Union, erfüllt werden.
- Ursachenanalyse
Die Spaltenherkunft bietet einen Mechanismus zur Rückverfolgung von Daten bis zu ihrer Quelle, was dazu beitragen kann, Fehlerpunkte zu identifizieren, die auf eine schlechte Datenqualität zurückzuführen sind, und die Anzahl der zu analysierenden Spalten während der Problembehandlung zu reduzieren.
Abfragen der ACCESS_HISTORY-Ansicht¶
Die folgenden Abschnitte enthalten Beispielabfragen für die ACCESS_HISTORY-Ansicht.
Beachten Sie, dass einige der Beispielabfragen nach der Spalte QUERY_START_TIME filtern, um die Abfrageleistung zu erhöhen. Eine weitere Möglichkeit, die Leistung zu erhöhen, ist die Einengung des Zeitbereichs, auf dem die Abfrage ausgeführt wird.
Beispiele: Leseabfragen¶
In den folgenden Unterabschnitten wird beschrieben, wie die ACCESS_HISTORY-Ansicht für Leseoperationen in den folgenden Anwendungsfälle abgefragt werden kann:
Abrufen des Zugriffsverlaufs für einen bestimmten Benutzer.
Vereinfachen der Compliance-Prüfungen für den Zugriff auf sensible Daten in den letzten 30 Tagen auf Basis von
object_id
(z. B. einer Tabellen-ID), um die folgenden Fragen zu beantworten:Wer hat auf die Daten zugegriffen?
Wann wurde auf die Daten zugegriffen?
Auf welche Spalten wurde zugegriffen?
Benutzerzugriffsverlauf zurückgeben¶
Gibt den Benutzerzugriffsverlauf zurück, sortiert nach Benutzer und Startzeit der Abfrage, beginnend mit dem letzten Zugriff.
SELECT user_name , query_id , query_start_time , direct_objects_accessed , base_objects_accessed FROM access_history ORDER BY 1, 3 desc ;
Compliance-Prüfungen vereinfachen¶
Die folgenden Beispiele helfen, die Einhaltung gesetzlicher Bestimmungen zu vereinfachen:
Fügen Sie den
object_id
-Wert hinzu, um festzustellen, wer in den letzten 30 Tagen auf eine sensible Tabelle zugegriffen hat:SELECT distinct user_name FROM access_history , lateral flatten(base_objects_accessed) f1 WHERE f1.value:"objectId"::int=<fill_in_object_id> AND f1.value:"objectDomain"::string='Table' AND query_start_time >= dateadd('day', -30, current_timestamp()) ;
Bestimmen Sie anhand des
object_id
-Wertes von32998411400350
, wann der Zugriff in den letzten 30 Tagen erfolgte:SELECT query_id , query_start_time FROM access_history , lateral flatten(base_objects_accessed) f1 WHERE f1.value:"objectId"::int=32998411400350 AND f1.value:"objectDomain"::string='Table' AND query_start_time >= dateadd('day', -30, current_timestamp()) ;
Bestimmen Sie anhand des
object_id
-Wertes von32998411400350
, auf welche Spalten in den letzten 30 Tagen zugegriffen wurde:SELECT distinct f4.value AS column_name FROM access_history , lateral flatten(base_objects_accessed) f1 , lateral flatten(f1.value) f2 , lateral flatten(f2.value) f3 , lateral flatten(f3.value) f4 WHERE f1.value:"objectId"::int=32998411400350 AND f1.value:"objectDomain"::string='Table' AND f4.key='columnName' ;
Beispiele: Schreiboperationen¶
In den folgenden Unterabschnitten wird beschrieben, wie die ACCESS_HISTORY-Ansicht für Schreiboperationen in den folgenden Anwendungsfälle abgefragt werden kann:
Laden von Daten aus einem Stagingbereich in eine Tabelle
Entladen von Daten aus einer Tabelle in einen Stagingbereich
Verwenden des PUT-Befehls zum Hochladen einer lokale Datei in einen Stagingbereich
Verwenden des GET-Befehls zum Abrufen von Datendateien aus einem Stagingbereich in ein lokales Verzeichnis
Verfolgen der Bewegung sensibler Stagingbereichsdaten
Daten aus einem Stagingbereich in eine Tabelle laden¶
Laden Sie eine Menge von Werten aus einer Datendatei in einen externen Cloudspeicher in Spalten einer Zieltabelle.
copy into table1(col1, col2) from (select t.$1, t.$2 from @mystage1/data1.csv.gz);
In den Spalten DIRECT_OBJECTS_ACCESSED und BASE_OBJECTS_ACCESSED wird angegeben, dass auf einen externen benannten Stagingbereich zugegriffen wurde:
{ "objectDomain": STAGE "objectName": "mystage1", "objectId": 1, "stageKind": "External Named" }
In der Spalte OBJECTS_MODIFIED wird angegeben, dass Daten in zwei Spalten der Tabelle geschrieben wurden:
{ "columns": [ { "columnName": "col1", "columnId": 1 }, { "columnName": "col2", "columnId": 2 } ], "objectId": 1, "objectName": "TEST_DB.TEST_SCHEMA.TABLE1", "objectDomain": TABLE }
Daten aus einer Tabelle in einen Stagingbereich entladen¶
Entladen Sie eine Menge von Werten aus einer Snowflake-Tabelle in den Cloudspeicher.
copy into @mystage1/data1.csv from table1;
In den Spalten DIRECT_OBJECTS_ACCESSED und BASE_OBJECTS_ACCESSED werden die Tabellenspalten angegeben, auf die zugegriffen wurde:
{ "objectDomain": TABLE "objectName": "TEST_DB.TEST_SCHEMA.TABLE1", "objectId": 123, "columns": [ { "columnName": "col1", "columnId": 1 }, { "columnName": "col2", "columnId": 2 } ] }
In der Spalte OBJECTS_MODIFIED wird der Stagingbereich angegeben, in den die abgerufenen Daten geschrieben wurden:
{ "objectId": 1, "objectName": "mystage1", "objectDomain": STAGE, "stageKind": "External Named" }
PUT-Befehl zum Hochladen einer lokalen Datei in einen Stagingbereich verwenden¶
Kopieren Sie eine Datendatei in einen internen (d. h. Snowflake) Stagingbereich.
put file:///tmp/data/mydata.csv @my_int_stage;
In den Spalten DIRECT_OBJECTS_ACCESSED und BASE_OBJECTS_ACCESSED wird der lokale Pfad zu der Datei angegeben, auf die zugegriffen wurde:
{ "location": "file:///tmp/data/mydata.csv" }
In der Spalte OBJECTS_MODIFIED wird der Stagingbereich angegeben, in den die abgerufenen Daten geschrieben wurden:
{ "objectId": 1, "objectName": "my_int_stage", "objectDomain": STAGE, "stageKind": "Internal Named" }
GET-Befehl zum Abrufen von Datendateien aus einem Stagingbereich in ein lokales Verzeichnis verwenden¶
Rufen Sie eine Datendatei aus einem internen Stagingbereich in ein Verzeichnis auf dem lokalen Rechner ab.
get @%mytable file:///tmp/data/;
In den Spalten DIRECT_OBJECTS_ACCESSED und BASE_OBJECTS_ACCESSED werden der Stagingbereich und das lokale Verzeichnis angegeben, auf die zugegriffen wurde:
{ "objectDomain": Stage "objectName": "mytable", "objectId": 1, "stageKind": "Table" }
In der Spalte OBJECTS_MODIFIED wird das Verzeichnis angegeben, in das die abgerufenen Daten geschrieben wurden:
{ "location": "file:///tmp/data/" }
Bewegung sensibler Stagingbereichsdaten verfolgen¶
Verfolgen Sie sensible Stagingbereichsdaten, während diese eine Serie von Abfragen in chronologischer Reihenfolge durchlaufen.
Führen Sie die folgenden Abfragen aus. Beachten Sie, dass fünf der Anweisungen auf Stagingbereiche zugreifen. Wenn Sie also die ACCESS_HISTORY-Ansicht für den Stagingbereichszugriff abfragen, sollte das Resultset fünf Zeilen enthalten.
use test_db.test_schema; create or replace table T1(content variant); insert into T1(content) select parse_json('{"name": "A", "id":1}'); -- T1 -> T6 insert into T6 select * from T1; -- S1 -> T1 copy into T1 from @S1; -- T1 -> T2 create table T2 as select content:"name" as name, content:"id" as id from T1; -- T1 -> S2 copy into @S2 from T1; -- S1 -> T3 create or replace table T3(customer_info variant); copy into T3 from @S1; -- T1 -> T4 create or replace table T4(name string, id string, address string); insert into T4(name, id) select content:"name", content:"id" from T1; -- T6 -> T7 create table T7 as select * from T6;Wobei:
T1
,T2
…T7
geben die Namen der Tabellen an.
S1
undS2
geben die Namen der Stagingbereiche an.
Führen Sie eine Abfrage des Zugriffsverlaufs aus, um den Zugriff auf Stagingbereich S1
zu ermitteln.
Die Daten der Spalten DIRECT_OBJECTS_ACCESSED, BASE_OBJECTS_ACCESSED und OBJECTS_MODIFIED sind in der folgenden Tabelle aufgeführt.
DIRECT_OBJECTS_ACCESSED
BASE_OBJECTS_ACCESSED
OBJECTS_MODIFIED
[ { "columns": [ { "columnId": 68610, "columnName": "CONTENT" } ], "objectDomain": "Table", "objectId": 66564, "objectName": "TEST_DB.TEST_SCHEMA.T1" } ] [ { "columns": [ { "columnId": 68610, "columnName": "CONTENT" } ], "objectDomain": "Table", "objectId": 66564, "objectName": "TEST_DB.TEST_SCHEMA.T1" } ] [ { "columns": [ { "columnId": 68611, "columnName": "CONTENT" } ], "objectDomain": "Table", "objectId": 66566, "objectName": "TEST_DB.TEST_SCHEMA.T6" } ] [ { "objectDomain": "Stage", "objectId": 117, "objectName": "TEST_DB.TEST_SCHEMA.S1", "stageKind": "External Named" } ] [ { "objectDomain": "Stage", "objectId": 117, "objectName": "TEST_DB.TEST_SCHEMA.S1", "stageKind": "External Named" } ] [ { "columns": [ { "columnId": 68610, "columnName": "CONTENT" } ], "objectDomain": "Table", "objectId": 66564, "objectName": "TEST_DB.TEST_SCHEMA.T1" } ] [ { "columns": [ { "columnId": 68610, "columnName": "CONTENT" } ], "objectDomain": "Table", "objectId": 66564, "objectName": "TEST_DB.TEST_SCHEMA.T1" } ] [ { "columns": [ { "columnId": 68610, "columnName": "CONTENT" } ], "objectDomain": "Table", "objectId": 66564, "objectName": "TEST_DB.TEST_SCHEMA.T1" } ] [ { "columns": [ { "columnId": 68613, "columnName": "ID" }, { "columnId": 68612, "columnName": "NAME" } ], "objectDomain": "Table", "objectId": 66568, "objectName": "TEST_DB.TEST_SCHEMA.T2" } ] [ { "columns": [ { "columnId": 68610, "columnName": "CONTENT" } ], "objectDomain": "Table", "objectId": 66564, "objectName": "TEST_DB.TEST_SCHEMA.T1" } ] [ { "columns": [ { "columnId": 68610, "columnName": "CONTENT" } ], "objectDomain": "Table", "objectId": 66564, "objectName": "TEST_DB.TEST_SCHEMA.T1" } ] [ { "objectDomain": "Stage", "objectId": 118, "objectName": "TEST_DB.TEST_SCHEMA.S2", "stageKind": "External Named" } ] [ { "objectDomain": "Stage", "objectId": 117, "objectName": "TEST_DB.TEST_SCHEMA.S1", "stageKind": "External Named" } ] [ { "objectDomain": "Stage", "objectId": 117, "objectName": "TEST_DB.TEST_SCHEMA.S1", "stageKind": "External Named" } ] [ { "columns": [ { "columnId": 68614, "columnName": "CUSTOMER_INFO" } ], "objectDomain": "Table", "objectId": 66570, "objectName": "TEST_DB.TEST_SCHEMA.T3" } ] [ { "columns": [ { "columnId": 68610, "columnName": "CONTENT" } ], "objectDomain": "Table", "objectId": 66564, "objectName": "TEST_DB.TEST_SCHEMA.T1" } ] [ { "columns": [ { "columnId": 68610, "columnName": "CONTENT" } ], "objectDomain": "Table", "objectId": 66564, "objectName": "TEST_DB.TEST_SCHEMA.T1" } ] [ { "columns": [ { "columnId": 68615, "columnName": "NAME" }, { "columnId": 68616, "columnName": "ID" } ], "objectDomain": "Table", "objectId": 66572, "objectName": "TEST_DB.TEST_SCHEMA.T4" } ] [ { "columns": [ { "columnId": 68611, "columnName": "CONTENT" } ], "objectDomain": "Table", "objectId": 66566, "objectName": "TEST_DB.TEST_SCHEMA.T6" } ] [ { "columns": [ { "columnId": 68611, "columnName": "CONTENT" } ], "objectDomain": "Table", "objectId": 66566, "objectName": "TEST_DB.TEST_SCHEMA.T6" } ] [ { "columns": [ { "columnId": 68618, "columnName": "CONTENT" } ], "objectDomain": "Table", "objectId": 66574, "objectName": "TEST_DB.TEST_SCHEMA.T7" } ]Beachten Sie bei diesem Abfragebeispiel Folgendes:
Verwendet einen rekursiven allgemeinen Tabellenausdruck.
Verwendet ein JOIN-Konstrukt anstelle einer USING-Klausel.
with access_history_flatten as ( select r.value:"objectId" as source_id, r.value:"objectName" as source_name, r.value:"objectDomain" as source_domain, w.value:"objectId" as target_id, w.value:"objectName" as target_name, w.value:"objectDomain" as target_domain, c.value:"columnName" as target_column, t.query_start_time as query_start_time from (select * from TEST_DB.ACCOUNT_USAGE.ACCESS_HISTORY) t, lateral flatten(input => t.BASE_OBJECTS_ACCESSED) r, lateral flatten(input => t.OBJECTS_MODIFIED) w, lateral flatten(input => w.value:"columns", outer => true) c ), sensitive_data_movements(path, target_id, target_name, target_domain, target_column, query_start_time) as -- Common Table Expression ( -- Anchor Clause: Get the objects that access S1 directly select f.source_name || '-->' || f.target_name as path, f.target_id, f.target_name, f.target_domain, f.target_column, f.query_start_time from access_history_flatten f where f.source_domain = 'Stage' and f.source_name = 'TEST_DB.TEST_SCHEMA.S1' and f.query_start_time >= dateadd(day, -30, date_trunc(day, current_date)) union all -- Recursive Clause: Recursively get all the objects that access S1 indirectly select sensitive_data_movements.path || '-->' || f.target_name as path, f.target_id, f.target_name, f.target_domain, f.target_column, f.query_start_time from access_history_flatten f join sensitive_data_movements on f.source_id = sensitive_data_movements.target_id and f.source_domain = sensitive_data_movements.target_domain and f.query_start_time >= sensitive_data_movements.query_start_time ) select path, target_name, target_id, target_domain, array_agg(distinct target_column) as target_columns from sensitive_data_movements group by path, target_id, target_name, target_domain;Die Abfrage liefert das folgende Resultset in Bezug auf die Datenbewegung in Stagingbereich
S1
:
PATH
TARGET_NAME
TARGET_ID
TARGET_DOMAIN
TARGET_COLUMNS
TEST_DB.TEST_SCHEMA.S1–>TEST_DB.TEST_SCHEMA.T1
TEST_DB.TEST_SCHEMA.T1
66564
Tabelle
[„CONTENT“]
TEST_DB.TEST_SCHEMA.S1–>TEST_DB.TEST_SCHEMA.T1–>TEST_DB.TEST_SCHEMA.S2
TEST_DB.TEST_SCHEMA.S2
118
Stagingbereich
[]
TEST_DB.TEST_SCHEMA.S1–>TEST_DB.TEST_SCHEMA.T1–>TEST_DB.TEST_SCHEMA.T2
TEST_DB.TEST_SCHEMA.T2
66568
Tabelle
[„NAME“,“ID“]
TEST_DB.TEST_SCHEMA.S1–>TEST_DB.TEST_SCHEMA.T1–>TEST_DB.TEST_SCHEMA.T4
TEST_DB.TEST_SCHEMA.T4
66572
Tabelle
[„ID“,“NAME“]
TEST_DB.TEST_SCHEMA.S1–>TEST_DB.TEST_SCHEMA.T3
TEST_DB.TEST_SCHEMA.T3
66570
Tabelle
[„CUSTOMER_INFO“]
Beispiel: Spaltenherkunft¶
Im folgenden Beispiel wird die ACCESS_HISTORY-Ansicht abgefragt, und die Funktion FLATTEN wird verwendet, um die Spalte OBJECTS_MODIFIED zu vereinfachen.
Führen Sie als repräsentatives Beispiel die folgende SQL-Abfrage in Ihrem Snowflake-Konto aus, um die folgende Tabelle zu erstellen, in der die nummerierten Kommentare Folgendes angeben:
// 1
: Ermittelt die Zuordnung zwischen dem FelddirectSources
und der Zielspalte.// 2
: Ermittelt die Zuordnung zwischen dem FeldbaseSources
und der Zielspalte.
// 1
select
directSources.value: "objectId" as source_object_id,
directSources.value: "objectName" as source_object_name,
directSources.value: "columnName" as source_column_name,
'DIRECT' as source_column_type,
om.value: "objectName" as target_object_name,
columns_modified.value: "columnName" as target_column_name
from
(
select
*
from
snowflake.account_usage.access_history
) t,
lateral flatten(input => t.OBJECTS_MODIFIED) om,
lateral flatten(input => om.value: "columns", outer => true) columns_modified,
lateral flatten(
input => columns_modified.value: "directSources",
outer => true
) directSources
union
// 2
select
baseSources.value: "objectId" as source_object_id,
baseSources.value: "objectName" as source_object_name,
baseSources.value: "columnName" as source_column_name,
'BASE' as source_column_type,
om.value: "objectName" as target_object_name,
columns_modified.value: "columnName" as target_column_name
from
(
select
*
from
snowflake.account_usage.access_history
) t,
lateral flatten(input => t.OBJECTS_MODIFIED) om,
lateral flatten(input => om.value: "columns", outer => true) columns_modified,
lateral flatten(
input => columns_modified.value: "baseSources",
outer => true
) baseSources
;
Rückgabewerte:
SOURCE_OBJECT_ID
SOURCE_OBJECT_NAME
SOURCE_COLUMN_NAME
SOURCE_COLUMN_TYPE
TARGET_OBJECT_NAME
TARGET_COLUMN_NAME
1
D.S.T0
NAME
BASE
D.S.T1
NAME
2
D.S.V1
NAME
DIRECT
D.S.T1
NAME
Beispiel: Richtlinienreferenzen von Zeilenzugriffsrichtlinien verfolgen¶
Gibt eine Zeile für jede Instanz zurück, wenn eine Zeilenzugriffsrichtlinie für eine Tabelle, Ansicht oder materialisierte Ansicht ohne Duplikate festgelegt ist:
use role accountadmin; select distinct obj_policy.value:"policyName"::VARCHAR as policy_name from snowflake.account_usage.access_history as ah , lateral flatten(ah.policies_referenced) as obj , lateral flatten(obj.value:"policies") as obj_policy ;
Beispiel: Richtlinienreferenzen von Maskierungsrichtlinien verfolgen¶
Gibt eine Zeile für jede Instanz zurück, wenn eine Maskierungsrichtlinie eine Spalte ohne Duplikate schützt. Beachten Sie, dass eine zusätzliche Vereinfachung (Flattening) erforderlich ist, da die POLICIES_REFERENCED-Spalten die Maskierungsrichtlinie einer Spalte eine Ebene tiefer als die Zeilenzugriffsrichtlinie einer Tabelle angeben:
use role accountadmin; select distinct policies.value:"policyName"::VARCHAR as policy_name from snowflake.account_usage.access_history as ah , lateral flatten(ah.policies_referenced) as obj , lateral flatten(obj.value:"columns") as columns , lateral flatten(columns.value:"policies") as policies ;
Beispiele: UDFs¶
Die folgenden UDF-Beispiele zeigen, wie die Account Usage-Ansicht ACCESS_HISTORY Daten erfasst:
Aufrufen einer UDF namens
get_product
.Einfügen des Produkts aus dem Aufruf der Funktion
get_product
in eine Tabelle namensmydb.tables.t1
.Freigeben von UDFs.
Aufrufen einer UDF¶
Angenommen, die folgende SQL-UDF berechnet das Produkt zweier Zahlen und speichert das Ergebnis im Schema namens mydb.udfs
:
CREATE FUNCTION MYDB.UDFS.GET_PRODUCT(num1 number, num2 number) RETURNS number AS $$ NUM1 * NUM2 $$ ;
Das direkte Aufrufen von get_product
führt zum Erfassen der UDF-Details in der Spalte DIRECT_OBJECTS_ACCESSED:
[ { "objectDomain": "FUNCTION", "objectName": "MYDB.UDFS.GET_PRODUCT", "objectId": "2", "argumentSignature": "(NUM1 NUMBER, NUM2 NUMBER)", "dataType": "NUMBER(38,0)" } ]
Dieses Beispiel ist analog zum Aufrufen einer gespeicherten Prozedur (unter diesem Thema).
UDF mit INSERT DML¶
Betrachten Sie die folgende INSERT-Anweisung, mit der die Spalten 1 und 2 in der Tabelle mydb.tables.t1
aktualisiert werden:
insert into t1(product) select get_product(c1, c2) from mydb.tables.t1;
Die ACCESS_HISTORY-Ansicht erfasst die get_product
-Funktion in folgenden Spalten:
Spalte DIRECT_OBJECTS_ACCESSED, da die Funktion in der SQL-Anweisung explizit genannt wird
Spalte OBJECTS_MODIFIED im Array
directSources
, da die Funktion die Quelle für die Werte ist, die in die Spalten eingefügt werden.
Außerdem wird auch Tabelle t1
in denselben Spalten erfasst:
DIRECT_OBJECTS_ACCESSED
OBJECTS_MODIFIED
[ { "objectDomain": "FUNCTION", "objectName": "MYDB.UDFS.GET_PRODUCT", "objectId": "2", "argumentSignature": "(NUM1 NUMBER, NUM2 NUMBER)", "dataType": "NUMBER(38,0)" }, { "objectDomain": "TABLE", "objectName": "MYDB.TABLES.T1", "objectId": 1, "columns": [ { "columnName": "c1", "columnId": 1 }, { "columnName": "c2", "columnId": 2 } ] } ] [ { "objectDomain": "TABLE", "objectName": "MYDB.TABLES.T1", "objectId": 2, "columns": [ { "columnId": "product", "columnName": "201", "directSourceColumns": [ { "objectDomain": "Table", "objectName": "MYDB.TABLES.T1", "objectId": "1", "columnName": "c1" }, { "objectDomain": "Table", "objectName": "MYDB.TABLES.T1", "objectId": "1", "columnName": "c2" }, { "objectDomain": "FUNCTION", "objectName": "MYDB.UDFS.GET_PRODUCT", "objectId": "2", "argumentSignature": "(NUM1 NUMBER, NUM2 NUMBER)", "dataType": "NUMBER(38,0)" } ], "baseSourceColumns":[] } ] } ]
Beispiel: Aufrufen einer gespeicherten Prozedur¶
Angenommen die folgende gespeicherte Prozedur ist im Schema namens mydb.procedures
gespeichert:
create or replace procedure get_id_value(name string) returns string not null language javascript as $$ var my_sql_command = "select id from A where name = '" + NAME + "'"; var statement = snowflake.createStatement( {sqlText: my_sql_command} ); var result = statement.execute(); result.next(); return result.getColumnValue(1); $$ ;
Der direkte Aufruf von my_procedure
führt dazu, dass die Details der Prozedur sowohl in der Spalte DIRECT_OBJECTS_ACCESSED als auch in der Spalte BASE_OBJECTS_ACCESSED wie folgt erfasst werden:
[ { "objectDomain": "PROCEDURE", "objectName": "MYDB.PROCEDURES.GET_ID_VALUE", "argumentSignature": "(NAME STRING)", "dataType": "STRING" } ]
Dieses Beispiel ist analog zum Aufrufen einer UDF (unter diesem Thema).
Beispiele: Verfolgen von Objekten, die durch eine DDL-Operation geändert wurden¶
Tag mit Schlüssel ALLOWED_VALUES erstellen¶
So erstellen Sie das Tag:
create tag governance.tags.pii allowed_values 'sensitive','public';
Spaltenwert:
{ "objectDomain": "TAG", "objectName": "governance.tags.pii", "objectId": "1", "operationType": "CREATE", "properties": { "allowedValues": { "sensitive": { "subOperationType": "ADD" }, "public": { "subOperationType": "ADD" } } } }
Bemerkung
Wenn Sie beim Erstellen des Tags keine zulässigen Werte angeben, ist das Feld properties
ein leeres Array (d. h. {}
).
Tabelle mit Tag und Maskierungsrichtlinie erstellen¶
So erstellen Sie die Tabelle mit einer Maskierungsrichtlinie auf der Spalte, einem Tag auf der Spalte und einem Tag auf der Tabelle:
create or replace table hr.data.user_info( email string with masking policy governance.policies.email_mask with tag (governance.tags.pii = 'sensitive') ) with tag (governance.tags.pii = 'sensitive');
Spaltenwert:
{ "objectDomain": "TABLE", "objectName": "hr.data.user_info", "objectId": "1", "operationType": "CREATE", "properties": { "tags": { "governance.tags.pii": { "subOperationType": "ADD", "objectId": { "value": "1" }, "tagValue": { "value": "sensitive" } } }, "columns": { "email": { objectId: { "value": 1 }, "subOperationType": "ADD", "tags": { "governance.tags.pii": { "subOperationType": "ADD", "objectId": { "value": "1" }, "tagValue": { "value": "sensitive" } } }, "maskingPolicies": { "governance.policies.email_mask": { "subOperationType": "ADD", "objectId": { "value": 2 } } } } } } }
Maskierungsrichtlinie auf ein Tag setzen¶
So legen Sie eine Maskierungsrichtlinie auf dem Tag fest (d. h. Tag-basiertes Maskieren):
alter tag governance.tags.pii set masking policy governance.policies.email_mask;
Spaltenwert:
{ "objectDomain": "TAG", "objectName": "governance.tags.pii", "objectId": "1", "operationType": "ALTER", "properties": { "maskingPolicies": { "governance.policies.email_mask": { "subOperationType": "ADD", "objectId": { "value": 2 } } } } }
Tabelle austauschen¶
So tauschen Sie die Tabelle mit dem Namen t2
gegen die Tabelle mit dem Namen t3
aus:
alter table governance.tables.t2 swap with governance.tables.t3;
Beachten Sie die beiden unterschiedlichen Datensätze in der Ansicht.
Datensatz 1:
{ "objectDomain": "Table", "objectId": 0, "objectName": "GOVERNANCE.TABLES.T2", "operationType": "ALTER", "properties": { "swapTargetDomain": { "value": "Table" }, "swapTargetId": { "value": 0 }, "swapTargetName": { "value": "GOVERNANCE.TABLES.T3" } } }
Datensatz 2:
{ "objectDomain": "Table", "objectId": 0, "objectName": "GOVERNANCE.TABLES.T3", "operationType": "ALTER", "properties": { "swapTargetDomain": { "value": "Table" }, "swapTargetId": { "value": 0 }, "swapTargetName": { "value": "GOVERNANCE.TABLES.T2" } } }
Maskierungsrichtlinie löschen¶
So löschen Sie die Maskierungsrichtlinie:
drop masking policy governance.policies.email_mask;
Spaltenwert:
{ "objectDomain" : "MASKING_POLICY", "objectName": "governance.policies.email_mask", "objectId" : "1", "operationType": "DROP", "properties" : {} }Bemerkung
Der Spaltenwert ist repräsentativ und gilt für eine DROP-Operation auf einem Tag und einer Zeilenzugriffsrichtlinie.
Das Feld
properties
ist ein leeres Array. Es enthält keine Informationen zu der Richtlinie vor der DROP-Operation.
Tag-Referenzen auf einer Spalte verfolgen¶
Fragen Sie die Spalte OBJECT_MODIFIED_BY_DDL ab, um zu überwachen, wie ein Tag auf eine Spalte gesetzt ist.
Als Administrator der Tabelle können Sie ein Tag auf eine Spalte setzen, das Tag wieder entfernen und das Tag mit einem anderen Zeichenfolgenwert aktualisieren:
alter table hr.tables.empl_info alter column email set tag governance.tags.test_tag = 'test'; alter table hr.tables.empl_info alter column email unset tag governance.tags.test_tag; alter table hr.tables.empl_info alter column email set tag governance.tags.data_category = 'sensitive';
Ändern Sie als Data Engineer den Tag-Wert:
alter table hr.tables.empl_info alter column email set tag governance.tags.data_category = 'public';
Fragen Sie die ACCESS_HISTORY-Ansicht ab, um die Änderungen zu überwachen:
select query_start_time, user_name, object_modified_by_ddl:"objectName"::string as table_name, 'EMAIL' as column_name, tag_history.value:"subOperationType"::string as operation, tag_history.key as tag_name, nvl((tag_history.value:"tagValue"."value")::string, '') as value from TEST_DB.ACCOUNT_USAGE.access_history ah, lateral flatten(input => ah.OBJECT_MODIFIED_BY_DDL:"properties"."columns"."EMAIL"."tags") tag_history where true and object_modified_by_ddl:"objectDomain" = 'Table' and object_modified_by_ddl:"objectName" = 'TEST_DB.TEST_SH.T' order by query_start_time asc;
Rückgabewerte:
+-----------------------------------+---------------+---------------------+-------------+-----------+-------------------------------+-----------+ | QUERY_START_TIME | USER_NAME | TABLE_NAME | COLUMN_NAME | OPERATION | TAG_NAME | VALUE | +-----------------------------------+---------------+---------------------+-------------+-----------+-------------------------------+-----------+ | Mon, Feb. 14, 2023 12:01:01 -0600 | TABLE_ADMIN | HR.TABLES.EMPL_INFO | EMAIL | ADD | GOVERNANCE.TAGS.TEST_TAG | test | | Mon, Feb. 14, 2023 12:02:01 -0600 | TABLE_ADMIN | HR.TABLES.EMPL_INFO | EMAIL | DROP | GOVERNANCE.TAGS.TEST_TAG | | | Mon, Feb. 14, 2023 12:03:01 -0600 | TABLE_ADMIN | HR.TABLES.EMPL_INFO | EMAIL | ADD | GOVERNANCE.TAGS.DATA_CATEGORY | sensitive | | Mon, Feb. 14, 2023 12:04:01 -0600 | DATA_ENGINEER | HR.TABLES.EMPL_INFO | EMAIL | ADD | GOVERNANCE.TAGS.DATA_CATEGORY | public | +-----------------------------------+---------------+---------------------+-------------+-----------+-------------------------------+-----------+