アクセス履歴¶
このトピックでは、Snowflakeのユーザーアクセス履歴に関する概念を説明します。
このトピックの内容:
概要¶
Snowflakeのアクセス履歴とは、ユーザークエリがデータを読み取り、 SQL ステートメントが INSERT、 UPDATE、 DELETE などのデータ書き込み操作を、ソースデータオブジェクトからターゲットデータオブジェクトに COPY コマンドのバリエーションとともに実行するときを指します。ユーザーアクセス履歴は、Account Usage ACCESS_HISTORY ビューをクエリすることで見つけられます。このビューにある記録は、法令順守の監査を容易にし、ユーザー(つまり、クエリ演算子)、クエリ、テーブルまたはビュー、列、およびデータの間に直接リンクがあるため、一般的で頻繁にアクセスされるテーブルと列に関する洞察を提供します。
ACCESS_HISTORY ビューの各行には、 SQL ステートメントごとに単一の記録が含まれます。この記録には、クエリが直接および間接的にアクセスした ソース列 (つまり、クエリのデータの基になるテーブル) および クエリ結果でユーザーが表示する 投影列 に関する情報が含まれています。クエリがクエリ結果の列にアクセスまたは投影しない場合、その列のデータはユーザーに提供されないため、ビュー記録にはその列が記録されません。例:
CREATE OR REPLACE VIEW v1 (vc1, vc2) AS
SELECT c1 as vc1,
c2 as vc2
FROM t
WHERE t.c3 > 0
;
テーブル列C1とC2は、ビューが直接アクセスするソース列で、 ACCESS_HISTORY ビューの BASE_OBJECTS_ACCESSED 列に記録されます。
ビュー列 VC1 と VC2 は、ユーザーがクエリするときに表示される投影列で、 ACCESS_HISTORY ビューの DIRECT_OBJECTS_ACCESSED 列に記録されます。
テーブル列C3は、ビューに含まれる行をフィルターするために使用され、この列は ACCESS_HISTORY ビューには記録されません。
SELECT * FROM v1;
のようなビューに対するクエリは、アクセスされた列として VC1 と VC2 を記録します。
注釈
Account Usage QUERY_HISTORY ビューの記録が、常に ACCESS_HISTORY ビューに記録されるとは限りません。SQL ステートメントの構造によって、Snowflakeが ACCESS_HISTORY ビューにエントリを記録するかどうかが決まります。
ACCESS_HISTORY ビューでSnowflakeがサポートする読み取りおよび書き込み操作の詳細については、ビュー 使用上の注意 をご参照ください。
読み取りおよび書き込み操作の追跡¶
ACCESS_HISTORY ビューには、次の列が含まれています。
QUERY_ID | QUERY_START_TIME | USER_NAME | DIRECT_OBJECTS_ACCESSED | BASE_OBJECTS_ACCESSED | OBJECTS_MODIFIED | OBJECT_MODIFIED_BY_DDL | POLICIES_REFERENCED
読み取り操作は最初の5列で追跡され、最後の列 OBJECTS_MODIFIED は、Snowflakeの列、テーブル、およびステージに関連するデータ書き込み情報を指定します。
Snowflakeのクエリとデータベースオブジェクトの作成方法により、Snowflakeが BASE_OBJECTS_ACCESSED 列、 OBJECTS_MODIFIED 列、 および DIRECT_OBJECTS_ACCESSED 列に対して返す情報が決まります。
同様に、クエリが行アクセスポリシーによって保護されたオブジェクトまたはマスキングポリシーによって保護された列を参照する場合、Snowflakeはポリシー情報を POLICIES_REFERENCED 列に記録します。
OBJECT_MODIFIED_BY_DDL 列は、データベース、スキーマ、テーブル、ビュー、および列に対する DDL 操作を記録します。これらの操作には、テーブルやビューに対する行アクセスポリシーを指定するステートメント、列に対するマスキングポリシーを指定するステートメント、オブジェクトや列に対するタグの更新(例: タグの設定、タグ値の変更)も含まれます。
列の詳細については、 ACCESS_HISTORY ビューの 列 セクションをご参照ください。
読む¶
読み取りクエリと、 ACCESS_HISTORY ビューがこの情報を記録する方法を理解するには、次のシナリオを検討してください。
一連のオブジェクト:
base_table
»view_1
»view_2
»view_3
。次のような
view_2
の読み取りクエリ:select * from view_2;
この例では、Snowflakeは次を返します。
DIRECT_OBJECTS_ACCESSED 列に
view_2
。クエリでview_2
が指定されているためです。BASE_OBJECTS_ACCESSED 列に
base_table
。これがview_2
にあるデータの元ソースであるためです。
view_1
と view_3
は、 DIRECT_OBJECTS_ACCESSED と BASE_OBJECTS_ACCESSED の列に含まれません。これらのビューはどちらもクエリに含まれておらず、 view_2
のデータのソースとして機能するベースオブジェクトではないためです。
書き込み¶
書き込み操作と、 ACCESS_HISTORY ビューがこの情報を記録する方法を理解するには、次のシナリオを検討してください。
データソース:
base_table
データソース(つまり、 CTAS)からテーブルを作成します。
create table table_1 as select * from base_table;
この例では、Snowflakeは次を返します。
テーブルは直接アクセスされ、データのソースであるため、 BASE_OBJECTS_ACCESSED 列と DIRECT_OBJECTS_ACCESSED 列内の
base_table
。テーブルの作成時に書き込まれた列を含む OBJECTS_MODIFIED 列に
table_1
。
列系統¶
列系統(つまり、列のアクセス履歴)は、Account Usage ACCESS_HISTORY ビューを拡張して、書き込み操作でソース列からターゲット列にデータがどのように流れるかを指定します。Snowflakeは、ソース列からのデータを参照する後続のすべてのテーブルオブジェクト(例: INSERT、 MERGE、 CTAS)を介して、ソース列からのデータを追跡します。ただし、系統チェーン内のオブジェクトがドロップされている場合を除きます。Snowflakeは、 ACCESS_HISTORY ビューの OBJECTS_MODIFIED 列を強化することで、列系統にアクセスできるようにします。
詳細については、次をご参照ください。
マスキングおよび行アクセスポリシー参照¶
POLICY_REFERENCED 列は、テーブルに設定された行アクセスポリシーまたは列に設定されたマスキングポリシーを持つオブジェクトを指定します。これには、行アクセスポリシーまたはマスキングポリシーによって保護されている中間オブジェクトが含まれます。Snowflakeは、テーブルまたは列に適用されるポリシーを記録します。
次のオブジェクトを検討してください。
t1
» v1
» v2
条件:
t1
はベーステーブル。v1
はベーステーブルから構築されたビュー。v2
はv1
から作成されたビュー。
ユーザーが v2
をクエリすると、 POLICIES_REFERENCED 列には、 v2
を保護する行アクセスポリシー、 v2
内の列を保護する各マスキングポリシー、または該当する場合は両方の種類のポリシーが記録されます。さらに、この列には、 t1
と v1
を保護するマスキングポリシーまたは行アクセスポリシーが記録されます。
これらの記録は、ポリシーで保護されたオブジェクトにアクセスされた方法をデータ管理者が理解するのに役立ちます。
POLICIES_REFERENCED 列は、 ACCESS_HISTORY ビューに追加の利点を提供します。
特定のクエリでユーザーがアクセスする、ポリシーで保護されたオブジェクトを識別します。
ポリシー監査プロセスを簡素化します。
ACCESS_HISTORY ビューをクエリすると、ユーザーがアクセスする保護されたオブジェクトと保護された列に関する情報を取得するために、他のAccount Usageビュー(例: POLICY_REFERENCES や QUERY_HISTORY)での複雑な結合が不必要になります。
サポートされている操作¶
ACCESS_HISTORY ビューがサポートする読み取りおよび書き込み操作の詳細については、 ACCESS_HISTORY ビュー の使用上の注意セクションをご参照ください。
利点¶
Snowflakeのアクセス履歴には、読み取りおよび書き込み操作に関する次の利点があります。
- データ検出
未使用のデータを検出して、データをアーカイブするか削除するかを決定します。
- 機密データの移動状況を追跡する
外部クラウドストレージの場所(例: Amazon S3バケット)からターゲットのSnowflakeテーブルへの、またはその逆のデータ移動を追跡します。
あるSnowflakeテーブルから異なるSnowflakeテーブルへの内部データの移動を追跡します。
機密データの移動をトレースした後、ポリシー(マスキング および 行アクセス)を適用してデータを保護し、 アクセス制御設定 を更新して、ステージへのアクセスをさらに規制します。テーブル、および タグ を設定して、機密データを含むステージ、テーブル、および列のコンプライアンス要件について追跡できるようにします。
- データ検証
レポート、ダッシュボード、およびチャートやグラフなどのデータ可視化製品の正確性と整合性は、データを元のソースまで追跡できるため、検証されます。
データスチュワードは、特定のテーブルまたはビューを削除または変更する前にユーザーに通知することもできます。
- コンプライアンス監査
GDPR や CCPA などのコンプライアンス規制を満たすために、テーブルまたはステージで書き込み操作を実行したSnowflakeユーザーを特定し、書き込み操作がいつ発生したかを特定します。
- 全体的なデータガバナンスを強化する
ACCESS_HISTORY ビューは、アクセスされたデータ、データアクセスがいつ行われたか、およびアクセスされたデータがデータソースオブジェクトからデータターゲットオブジェクトにどのように移動したかを統一的に示します。
列系統には、次の追加の利点があります。
- 派生オブジェクトの保護
データスチュワードは、派生オブジェクト(例: CTAS)を作成した後に追加作業なしで、機密性の高いソース列に簡単に タグ付け できます。その後、データスチュワードは、機密性の高い列を含むテーブルを 行アクセスポリシー で保護するか、機密性の高い列自体を マスキングポリシー または タグベースのマスキングポリシー で保護できます。
- 機密列のコピー頻度
データプライバシー担当者は、機密データを含む列のオブジェクト数(例: テーブル1つ、ビュー2つ)を迅速に判断できます。機密データを含む列がテーブルオブジェクトに何回出現するかを知ることで、データプライバシー担当者は、規制コンプライアンス基準をどのように満たしているかを証明できます(例: 欧州連合の一般データ保護規則(GDPR)基準に準拠)。
- 根本原因の分析
列系統は、データをソースまで追跡するメカニズムを提供します。これは、データ品質の低下に起因する障害点を特定し、トラブルシューティングプロセス中に分析する列の数を減らすのに役立ちます。
ACCESS_HISTORY ビューのクエリ¶
次のセクションでは、 ACCESS_HISTORY ビューのクエリ例を示します。
一部のサンプルクエリは、クエリのパフォーマンスを向上させるために QUERY_START_TIME 列でフィルターすることに注意してください。パフォーマンスを向上させるもう1つのオプションは、より狭い時間範囲でクエリを実行することです。
例: クエリの読み取り¶
以下のサブセクションでは、次のユースケースで読み取り操作のために ACCESS_HISTORY ビューをクエリする方法について詳しく説明します。
特定のユーザーのアクセス履歴を取得します。
次の質問に答えるために、
object_id
(例: テーブルID)に基づいて、過去30日間の機密データアクセスのコンプライアンス監査を促進します。誰がデータにアクセスしましたか?
データはいつアクセスされましたか?
どの列がアクセスされましたか?
ユーザーのアクセス履歴を返す¶
最新のアクセスから開始して、ユーザーとクエリの開始時刻の順にユーザーアクセス履歴を返します。
SELECT user_name , query_id , query_start_time , direct_objects_accessed , base_objects_accessed FROM access_history ORDER BY 1, 3 desc ;
コンプライアンス監査を促進する¶
次の例は、コンプライアンス監査を容易にするために役立ちます。
object_id
値を追加して、過去30日間に機密テーブルにアクセスしたユーザーを特定します。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()) ;
32998411400350
のobject_id
値を使用して、過去30日間にアクセスがあった時期を特定します。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()) ;
32998411400350
のobject_id
値を使用して、過去30日間にアクセスされた列を特定します。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' ;
例: 書き込み操作¶
以下のサブセクションでは、次のユースケースで書き込み操作のために ACCESS_HISTORY ビューをクエリする方法について詳しく説明します。
ステージからテーブルにデータをロードします。
テーブルからステージにデータをアンロードします。
PUT コマンドを使用して、ローカルファイルをステージにアップロードします。
GET コマンドを使用して、ステージからローカルディレクトリにデータファイルを取得します。
機密性の高いステージデータの動きを追跡します。
ステージからテーブルにデータをロードする¶
外部クラウドストレージのデータファイルからターゲットテーブルの列に値のセットをロードします。
copy into table1(col1, col2) from (select t.$1, t.$2 from @mystage1/data1.csv.gz);
DIRECT_OBJECTS_ACCESSED 列と BASE_OBJECTS_ACCESSED 列は、外部の名前付きステージにアクセスしたことを指定します。
{ "objectDomain": STAGE "objectName": "mystage1", "objectId": 1, "stageKind": "External Named" }
OBJECTS_MODIFIED 列は、データがテーブルの2つの列に書き込まれたことを指定します。
{ "columns": [ { "columnName": "col1", "columnId": 1 }, { "columnName": "col2", "columnId": 2 } ], "objectId": 1, "objectName": "TEST_DB.TEST_SCHEMA.TABLE1", "objectDomain": TABLE }
テーブルからステージにデータをアンロードする¶
値のセットをSnowflakeテーブルからクラウドストレージにアンロードします。
copy into @mystage1/data1.csv from table1;
DIRECT_OBJECTS_ACCESSED 列と BASE_OBJECTS_ACCESSED 列は、アクセスされたテーブル列を指定します。
{ "objectDomain": TABLE "objectName": "TEST_DB.TEST_SCHEMA.TABLE1", "objectId": 123, "columns": [ { "columnName": "col1", "columnId": 1 }, { "columnName": "col2", "columnId": 2 } ] }
OBJECTS_MODIFIED 列は、アクセスされたデータが書き込まれたステージを指定します。
{ "objectId": 1, "objectName": "mystage1", "objectDomain": STAGE, "stageKind": "External Named" }
PUT コマンドを使用して、ローカルファイルをステージにアップロードする¶
データファイルを内部(つまり、Snowflake)ステージにコピーします。
put file:///tmp/data/mydata.csv @my_int_stage;
DIRECT_OBJECTS_ACCESSED 列と BASE_OBJECTS_ACCESSED 列は、アクセスされたファイルへのローカルパスを指定します。
{ "location": "file:///tmp/data/mydata.csv" }
OBJECTS_MODIFIED 列は、アクセスされたデータが書き込まれたステージを指定します。
{ "objectId": 1, "objectName": "my_int_stage", "objectDomain": STAGE, "stageKind": "Internal Named" }
GET コマンドを使用して、ステージからローカルディレクトリにデータファイルを取得する¶
内部ステージからローカルマシン上のディレクトリにデータファイルを取得します。
get @%mytable file:///tmp/data/;
DIRECT_OBJECTS_ACCESSED 列と BASE_OBJECTS_ACCESSED 列は、アクセスされたステージとローカルディレクトリを指定します。
{ "objectDomain": Stage "objectName": "mytable", "objectId": 1, "stageKind": "Table" }
OBJECTS_MODIFIED 列は、アクセスされたデータが書き込まれたディレクトリを指定します。
{ "location": "file:///tmp/data/" }
機密性の高いステージデータの動きの追跡¶
時系列で実行される一連のクエリを移動するときに、機密性の高いステージデータを追跡します。
次のコマンドを実行します。5つのステートメントがステージデータにアクセスすることに注意してください。したがって、ステージアクセスの ACCESS_HISTORY ビューをクエリする場合、結果セットには5行が含まれている必要があります。
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;条件:
T1
、T2
...T7
は、テーブルの名前を指定します。
S1
およびS2
は、ステージの名前を指定します。
アクセス履歴をクエリし、ステージ S1
へのアクセスを決定します。
次のテーブルに、 DIRECT_OBJECTS_ACCESSED、 BASE_OBJECTS_ACCESSED、および OBJECTS_MODIFIED 列のデータを示します。
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" } ]クエリの例については、次の点に注意してください。
再帰共通テーブル式 を使用します。
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;クエリは、ステージ
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
テーブル
["CONTENT"]
TEST_DB.TEST_SCHEMA.S1-->TEST_DB.TEST_SCHEMA.T1-->TEST_DB.TEST_SCHEMA.S2
TEST_DB.TEST_SCHEMA.S2
118
ステージ
[]
TEST_DB.TEST_SCHEMA.S1-->TEST_DB.TEST_SCHEMA.T1-->TEST_DB.TEST_SCHEMA.T2
TEST_DB.TEST_SCHEMA.T2
66568
テーブル
["NAME","ID"]
TEST_DB.TEST_SCHEMA.S1-->TEST_DB.TEST_SCHEMA.T1-->TEST_DB.TEST_SCHEMA.T4
TEST_DB.TEST_SCHEMA.T4
66572
テーブル
["ID","NAME"]
TEST_DB.TEST_SCHEMA.S1-->TEST_DB.TEST_SCHEMA.T3
TEST_DB.TEST_SCHEMA.T3
66570
テーブル
["CUSTOMER_INFO"]
例: 列系統¶
次の例では、 ACCESS_HISTORY ビューをクエリし、 FLATTEN 関数を使用して OBJECTS_MODIFIED 列をフラット化します。
代表的な例として、Snowflakeアカウントで次の SQL クエリを実行して、以下のテーブルを生成します。ここで、番号付きのコメントは次を示しています。
// 1
:directSources
フィールドとターゲット列の間のマッピングを取得します。// 2
:baseSources
フィールドとターゲット列の間のマッピングを取得します。
// 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
;
戻り値:
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
例: 行アクセスポリシー参照を追跡する¶
テーブル、ビュー、またはマテリアライズドビューに行アクセスポリシーが重複することなく設定されている場合は、各インスタンスに対して行を返します。
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 ;
例: マスキングポリシー参照を追跡する¶
マスキングポリシーによって列が重複することなく保護されている場合は、各インスタンスに対して行を返します。POLICIES_REFERENCED 列は、テーブルの行アクセスポリシーよりも1レベル深い列のマスキングポリシーを指定するため、追加のフラット化が必要であることに注意してください。
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 ;
例: UDFs¶
これらの UDF の例は、Account Usage ACCESS_HISTORY ビューが記録する方法を示しています。
get_product
という名前の UDF を 呼び出し ています。get_product
関数を呼び出した積をmydb.tables.t1
という名前のテーブルに 挿入 します。共有 UDFs。
UDF を呼び出す¶
2つの数値の積を計算する次の SQL UDF を検討し、それが mydb.udfs
という名前のスキーマに格納されていると仮定します。
CREATE FUNCTION MYDB.UDFS.GET_PRODUCT(num1 number, num2 number) RETURNS number AS $$ NUM1 * NUM2 $$ ;
get_product
を直接 呼び出す と、 UDF の詳細が DIRECT_OBJECTS_ACCESSED 列に記録されます。
[ { "objectDomain": "FUNCTION", "objectName": "MYDB.UDFS.GET_PRODUCT", "objectId": "2", "argumentSignature": "(NUM1 NUMBER, NUM2 NUMBER)", "dataType": "NUMBER(38,0)" } ]
この例は、 ストアドプロシージャの呼び出し (このトピック内)に類似しています。
INSERT DML を使用した UDF¶
mydb.tables.t1
という名前のテーブルの1および2という名前の列を更新するために、次の INSERT ステートメントを検討してください。
insert into t1(product) select get_product(c1, c2) from mydb.tables.t1;
ACCESS_HISTORY ビューは、 get_product
関数を次の場所に記録します。
DIRECT_OBJECTS_ACCESSED 列。これは、 SQL ステートメントで明示的に関数の名前が付けられているため。また、
directSources
配列にある OBJECTS_MODIFIED 列。これは、関数が列に挿入される値のソースであるため。
同様に、テーブル t1
は次の同じ列に記録されます。
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":[] } ] } ]
例: ストアドプロシージャを呼び出す¶
次のストアドプロシージャを検討し、 mydb.procedures
という名前のスキーマに格納されていると仮定します。
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); $$ ;
my_procedure
を直接 呼び出す と、次のように DIRECT_OBJECTS_ACCESSED 列と BASE_OBJECTS_ACCESSED 列の両方にプロシージャの詳細が記録されます。
[ { "objectDomain": "PROCEDURE", "objectName": "MYDB.PROCEDURES.GET_ID_VALUE", "argumentSignature": "(NAME STRING)", "dataType": "STRING" } ]
この例は、 UDF の呼び出し (このトピック内)に類似しています。
例: DDL 操作によって変更されたオブジェクトの追跡¶
ALLOWED_VALUES でタグを作成する¶
タグを作成します。
create tag governance.tags.pii allowed_values 'sensitive','public';
列の値:
{ "objectDomain": "TAG", "objectName": "governance.tags.pii", "objectId": "1", "operationType": "CREATE", "properties": { "allowedValues": { "sensitive": { "subOperationType": "ADD" }, "public": { "subOperationType": "ADD" } } } }
注釈
タグの作成時に許容値を指定しなかった場合、 properties
フィールドは空の配列になります(つまり、 {}
)。
タグおよびマスキングポリシーでテーブルを作成する¶
列にマスキングポリシー、列にタグ、テーブルにタグを付けてテーブルを作成します。
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');
列の値:
{ "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 } } } } } } }
タグにマスキングポリシーを設定する¶
タグにマスキングポリシーを設定します(例: タグベースのマスキング)。
alter tag governance.tags.pii set masking policy governance.policies.email_mask;
列の値:
{ "objectDomain": "TAG", "objectName": "governance.tags.pii", "objectId": "1", "operationType": "ALTER", "properties": { "maskingPolicies": { "governance.policies.email_mask": { "subOperationType": "ADD", "objectId": { "value": 2 } } } } }
テーブルを入れ替える¶
t2
という名前のテーブルを t3
という名前のテーブルと入れ替えます。
alter table governance.tables.t2 swap with governance.tables.t3;
ビューの2つの異なる記録に注目してください。
記録1:
{ "objectDomain": "Table", "objectId": 0, "objectName": "GOVERNANCE.TABLES.T2", "operationType": "ALTER", "properties": { "swapTargetDomain": { "value": "Table" }, "swapTargetId": { "value": 0 }, "swapTargetName": { "value": "GOVERNANCE.TABLES.T3" } } }
記録2:
{ "objectDomain": "Table", "objectId": 0, "objectName": "GOVERNANCE.TABLES.T3", "operationType": "ALTER", "properties": { "swapTargetDomain": { "value": "Table" }, "swapTargetId": { "value": 0 }, "swapTargetName": { "value": "GOVERNANCE.TABLES.T2" } } }
マスキングポリシーをドロップする¶
マスキングポリシーをドロップします。
drop masking policy governance.policies.email_mask;
列の値:
{ "objectDomain" : "MASKING_POLICY", "objectName": "governance.policies.email_mask", "objectId" : "1", "operationType": "DROP", "properties" : {} }注釈
列の値は代表的なもので、タグと行のアクセスポリシーに対する DROP 操作に適用されます。
properties
フィールドは空の配列で、 DROP 操作前のポリシーに関する情報は提供されません。
列のタグ参照を追跡する¶
タグがどのように列に設定されているかをモニターするために、 OBJECT_MODIFIED_BY_DDL 列をクエリします。
テーブル管理者として、列にタグを設定し、タグの設定を解除し、異なる文字列値でタグを更新します。
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';
データエンジニアとして、タグの値を変更します。
alter table hr.tables.empl_info alter column email set tag governance.tags.data_category = 'public';
ACCESS_HISTORY ビューをクエリして変更をモニターします。
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;
戻り値:
+-----------------------------------+---------------+---------------------+-------------+-----------+-------------------------------+-----------+ | 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 | +-----------------------------------+---------------+---------------------+-------------+-----------+-------------------------------+-----------+