SnowConvert: パフォーマンスレビューのメッセージ¶
パフォーマンスレビュー(PRF)の問題は、 SnowConvert がソースコードをSnowflakeに正常に翻訳する場合に発生しますが、結果として生成されるコードはSnowflakeの環境に最適化されていない可能性があります。変換したコードに PRF がある場合は、その部分を注意深く見直し、パフォーマンスを向上させるために書き換えられないかどうかを検討することをお勧めします。
SSC-PRF-0006¶
重大性¶
なし
説明¶
このメッセージは、クエリにカーソル定義が含まれている場合に表示されます。カーソル式が評価されると、カーソルは戻り、自動的にネストされたカーソルを開きます。カーソル式の詳細については、 Oracleカーソル式 をご参照ください。
コードの例:¶
SELECT
category_id,
category_name,
CURSOR (
SELECT
product_id,
product_name || ', ' || category_id
FROM
products e
WHERE
e.category_id = d.category_id
) EMP_CUR
FROM
categories d;
SELECT
category_id,
category_name,
--** SSC-PRF-0006 - NESTED CURSOR INSIDE QUERY IS NOT SUPPORTED IN SNOWFLAKE. **
CURSOR
!!!RESOLVE EWI!!! /*** SSC-EWI-0108 - THE FOLLOWING SUBQUERY MATCHES AT LEAST ONE OF THE PATTERNS CONSIDERED INVALID AND MAY PRODUCE COMPILATION ERRORS ***/!!! (
SELECT
product_id,
NVL(
product_name :: STRING, '') || ', ' || NVL(category_id :: STRING, '')
FROM
products e
WHERE
e.category_id = d.category_id
) EMP_CUR
FROM
categories d;
推奨事項¶
ネストされたカーソルはパフォーマンスに悪影響を及ぼし、コードを複雑にするため避けるようにします。
ネストされたカーソルの代わりに、 SQL のような代替機能を使用します。
SQL 関数
結合
サブクエリ
ウィンドウ関数
共通テーブル式(CTEs)
再帰クエリ。これらの選択肢は、大量のデータを処理するのに適しています。
その他のサポートについては、 snowconvert-support@snowflake.com にお問い合わせください
SSC-PRF-0002¶
説明¶
Snowflakeで照合順序を使用すると、特に WHERE 句で使用した場合にクエリのパフォーマンスに影響を与えることがあります。照合順序がパフォーマンスに与える影響については、 照合順序の使用におけるパフォーマンスへの影響 ドキュメントをご参照ください。
大文字と小文字を区別しない照合順序で列を作成すると警告が表示されます。クエリでこの列を使用するとパフォーマンスが低下する可能性があります。
コードの例:¶
CREATE TABLE exampleTable
(
col1 CHAR(10),
col2 CHAR(20) COLLATE 'en-ci' /*** SSC-PRF-0002 - CASE INSENSITIVE COLUMNS CAN DECREASE THE PERFORMANCE OF QUERIES ***/
)
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"teradata"}}'
;
Oracle¶
CREATE TABLE exampleTable (
col1 VARCHAR(50) COLLATE BINARY_CI,
col2 VARCHAR(50) COLLATE BINARY_CS
);
CREATE OR REPLACE TABLE exampleTable (
col1 VARCHAR(50) COLLATE BINARY_CI /*** SSC-PRF-0002 - CASE INSENSITIVE COLUMNS CAN DECREASE THE PERFORMANCE OF QUERIES ***/,
col2 VARCHAR(50) COLLATE BINARY_CS
)
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"oracle"}}'
;
SQL Server¶
CREATE TABLE exampleTable (
col1 VARCHAR(50) COLLATE Latin1_General_CI_AS,
col2 VARCHAR(50) COLLATE Latin1_General_CS_AS
);
CREATE OR REPLACE TABLE exampleTable (
col1 VARCHAR(50) COLLATE 'EN-CI-AS' /*** SSC-PRF-0002 - CASE INSENSITIVE COLUMNS CAN DECREASE THE PERFORMANCE OF QUERIES ***/,
col2 VARCHAR(50) COLLATE 'EN-CS-AS'
)
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
;
推奨事項¶
大文字と小文字を区別しない照合順序がアプリケーションのパフォーマンスにどのような影響を与えるかを評価します。影響が大きい場合は、コードを修正してその使用を避けることを検討してください。パフォーマンスへの影響が最小限であれば、この警告は無視してかまいません。
その他のサポートについては、 snowconvert-support@snowflake.com にお問い合わせください
SSC-PRF-0003¶
重大性¶
低
説明¶
この警告は、ループ内で FETCH
ステートメントが検出された場合に表示されます。FETCH
ステートメントは、結果セットから個々の行を取り出し、1行ずつ処理することができます。
ループ内でカーソルを使用する大容量データセットの処理は、特に以下のような場合に複雑になります。
複数のテーブルの結合が関係する場合
複雑な計算が必要な場合
大量の行の処理が必要な場合
このアプローチはパフォーマンスの問題につながる可能性があり、データ量が増大するにつれて保守が困難になる可能性があります。
コード例¶
Teradata¶
REPLACE PROCEDURE teradata_fetch_inside_loop()
DYNAMIC RESULT SETS 1
BEGIN
DECLARE col_name VARCHAR(200);
DECLARE col_int INTEGER DEFAULT 0;
DECLARE cursor_var CURSOR FOR SELECT some_column FROM tabla1;
WHILE (col_int <> 0) DO
FETCH cursor_var INTO col_name;
SET col_int = col_int + 1;
END WHILE;
END;
--** SSC-FDM-0007 - MISSING DEPENDENT OBJECT "tabla1" **
CREATE OR REPLACE PROCEDURE teradata_fetch_inside_loop ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "teradata", "convertedOn": "07/04/2024" }}'
EXECUTE AS CALLER
AS
$$
DECLARE
col_name VARCHAR(200);
col_int INTEGER DEFAULT 0;
BEGIN
LET cursor_var CURSOR
FOR
SELECT
some_column FROM
tabla1;
WHILE (col_int <> 0) LOOP
--** SSC-PRF-0003 - FETCH INSIDE A LOOP IS CONSIDERED A COMPLEX PATTERN, THIS COULD DEGRADE SNOWFLAKE PERFORMANCE. **
FETCH cursor_var INTO col_name;
col_int := col_int + 1;
END LOOP;
END;
$$;
Oracle¶
CREATE PROCEDURE oracle_fetch_inside_loop
IS
var1 table1.column1%TYPE;
CURSOR cursor1 IS SELECT COLUMN_NAME FROM table1;
BEGIN
WHILE true LOOP
FETCH cursor1 INTO var1;
EXIT WHEN cursor1%NOTFOUND;
END LOOP;
END;
CREATE OR REPLACE PROCEDURE oracle_fetch_inside_loop ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"oracle"}}'
EXECUTE AS CALLER
AS
$$
DECLARE
var1 VARIANT !!!RESOLVE EWI!!! /*** SSC-EWI-OR0129 - TYPE ATTRIBUTE 'table1.column1%TYPE' COULD NOT BE RESOLVED, SO IT WAS TRANSFORMED TO VARIANT ***/!!!;
cursor1 CURSOR
FOR
SELECT COLUMN_NAME FROM
table1;
BEGIN
WHILE (true) LOOP
--** SSC-PRF-0003 - FETCH INSIDE A LOOP IS CONSIDERED A COMPLEX PATTERN, THIS COULD DEGRADE SNOWFLAKE PERFORMANCE. **
FETCH cursor1 INTO
:var1;
IF (var1 IS NULL) THEN
EXIT;
END IF;
END LOOP;
END;
$$;
SQL Server¶
CREATE OR ALTER PROCEDURE transact_fetch_inside_loop
AS
BEGIN
DECLARE cursor1 CURSOR
FOR SELECT col1 FROM my_table;
WHILE 1=0
BEGIN
FETCH NEXT FROM @cursor1 INTO @variable1;
END
END;
CREATE OR REPLACE PROCEDURE transact_fetch_inside_loop ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
DECLARE
--** SSC-FDM-TS0013 - SNOWFLAKE SCRIPTING CURSOR ROWS ARE NOT MODIFIABLE **
cursor1 CURSOR
FOR
SELECT
col1
FROM
my_table;
BEGIN
WHILE (1=0) LOOP
--** SSC-PRF-0003 - FETCH INSIDE A LOOP IS CONSIDERED A COMPLEX PATTERN, THIS COULD DEGRADE SNOWFLAKE PERFORMANCE. **
FETCH
CURSOR1
INTO
:VARIABLE1;
END LOOP;
END;
$$;
推奨事項¶
複雑なパターンを避けるには、ループの代わりにセットベースの操作を使います。SQL ステートメント(SELECT、 UPDATE、 DELETE)に WHERE 句を記述して、1行ずつ処理するのではなく、複数行を一度に処理するようにします。
CREATE OR REPLACE PROCEDURE cursor_fetch_inside_loop
AS
record_employee employees%rowtype;
CURSOR emp_cursor IS SELECT * FROM employees;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO record_employee;
EXIT WHEN emp_cursor%notfound;
INSERT INTO new_employees VALUES (record_employee.first_name, record_employee.last_name);
END LOOP;
CLOSE emp_cursor;
END;
CREATE OR REPLACE PROCEDURE cursor_fetch_inside_loop ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"oracle"}}'
EXECUTE AS CALLER
AS
$$
DECLARE
record_employee OBJECT !!!RESOLVE EWI!!! /*** SSC-EWI-0036 - ROWTYPE DATA TYPE CONVERTED TO OBJECT ***/!!! := OBJECT_CONSTRUCT();
emp_cursor CURSOR
FOR
SELECT
OBJECT_CONSTRUCT( *) sc_cursor_record FROM
employees;
BEGIN
OPEN emp_cursor;
LOOP
--** SSC-PRF-0003 - FETCH INSIDE A LOOP IS CONSIDERED A COMPLEX PATTERN, THIS COULD DEGRADE SNOWFLAKE PERFORMANCE. **
FETCH emp_cursor INTO
:record_employee;
IF (record_employee IS NULL) THEN
EXIT;
END IF;
INSERT INTO new_employees
SELECT
:record_employee:FIRST_NAME,
:record_employee:LAST_NAME;
END LOOP;
CLOSE emp_cursor;
END;
$$;
セットベースの操作は、データをより効率的に処理するために使用できます。
CREATE OR REPLACE PROCEDURE cursor_fetch_inside_loop AS
BEGIN
INSERT INTO new_employees (first_name, last_name)
SELECT first_name, last_name FROM employees;
END;
セットベースの操作を使ってデータを操作することができます。
CREATE OR REPLACE PROCEDURE cursor_fetch_inside_loop ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"oracle"}}'
EXECUTE AS CALLER
AS
$$
BEGIN
INSERT INTO new_employees(first_name, last_name)
SELECT first_name, last_name FROM
employees;
END;
$$;
推奨事項¶
その他のサポートについては、 snowconvert-support@snowflake.com にお問い合わせください
SSC-PRF-0007¶
説明¶
CLUSTER BY コマンドの使用時に発生する可能性のあるパフォーマンスの問題を識別します。
コード例¶
Teradata:¶
CREATE MULTISET TABLE T_2008,
NO FALLBACK,
NO BEFORE JOURNAL,
NO AFTER JOURNAL,
CHECKSUM = DEFAULT,
DEFAULT MERGEBLOCKRATIO
(
COL1 NUMBER(20,0) NOT NULL,
COL2 INTEGER,
COL3 VARCHAR(4) CHARACTER SET LATIN NOT CASESPECIFIC,
COL4 DATE FORMAT 'YYYY-MM-DD'
)
PRIMARY INDEX
(
COL1, COL2
)
PARTITION BY ( RANGE_N(COL4 BETWEEN DATE '2010-01-01' AND DATE '2025-12-31' EACH INTERVAL '1' YEAR ),
CASE_N(
COL3 = 'T',
COL3 = 'M',
COL3 = 'L') ); -- PARTITION BY transformed to CLUSTER BY
Snowflake:¶
CREATE OR REPLACE TABLE T_2008
(
COL1 NUMBER(20,0) NOT NULL,
COL2 INTEGER,
COL3 VARCHAR(4) COLLATE 'en-cs',
COL4 DATE
)
--** SSC-PRF-0007 - PERFORMANCE REVIEW - CLUSTER BY **
CLUSTER BY (
!!!RESOLVE EWI!!! /*** SSC-EWI-0031 - RANGE_N FUNCTION NOT SUPPORTED ***/!!!
RANGE_N(COL4 BETWEEN DATE '2010-01-01' AND DATE '2025-12-31' EACH INTERVAL '1' YEAR ),
!!!RESOLVE EWI!!! /*** SSC-EWI-0031 - CASE_N FUNCTION NOT SUPPORTED ***/!!!
CASE_N(
COL3 = 'T',
COL3 = 'M',
COL3 = 'L'))
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "teradata", "convertedOn": "12/16/2024", "domain": "test" }}'
; -- PARTITION BY transformed to CLUSTER BY
Transact:¶
CREATE TABLE my_table (
enterprise_cif INT,
name NVARCHAR(100),
address NVARCHAR(255),
created_at DATETIME
)
WITH (
DISTRIBUTION = HASH(enterprise_cif),
CLUSTERED INDEX (enterprise_cif)
);
Snowflake:¶
CREATE OR REPLACE TABLE my_table (
enterprise_cif INT,
name VARCHAR(100),
address VARCHAR(255),
created_at TIMESTAMP_NTZ(3)
)
--** SSC-PRF-0007 - PERFORMANCE REVIEW - CLUSTER BY **
CLUSTER BY (enterprise_cif)
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "transact", "convertedOn": "10/09/2024" }}'
;
推奨事項¶
潜在的なパフォーマンスのボトルネックを特定するためにコードを見直しましょう。パフォーマンス最適化の詳細については、 このドキュメント をご参照ください。
その他のサポートについては、サポートチーム snowconvert-support@snowflake.com にお問い合わせください
SSC-PRF-0004¶
重大性¶
なし
説明¶
この警告は、ステートメントにループ操作用のカーソルが含まれていることを示します。ループ用のカーソルは、クエリ結果を一度に1行ずつ処理するプログラミング構造で、結果セットから個々の記録を扱うことができます。
この警告は、ループ用カーソルにおける潜在的なパフォーマンス問題を特定するのに役立ちます。パフォーマンスの問題は、以下のような場合に発生する可能性があります。
カーソル内の SELECT ステートメントが大容量のデータセットを返す場合
ループに複雑な操作が含まれている場合
ループにネストされたループが含まれている場合
SnowConvert はこれらのパターンを検出することができますが、効率的な実行を保証するためにコードを見直し、最適化する必要があります。
コード例¶
Teradata¶
REPLACE PROCEDURE teradata_cursor_for_loop()
BEGIN
FOR fUsgClass AS cUsgClass CURSOR FOR
(SELECT col1
FROM sample_table)
DO
SET var1 = fUsgClass.col1;
END FOR;
END;
CREATE OR REPLACE PROCEDURE teradata_cursor_for_loop ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"teradata"}}'
EXECUTE AS CALLER
AS
$$
BEGIN
LET cUsgClass CURSOR
FOR
SELECT
col1
FROM
sample_table;
--** SSC-PRF-0004 - THIS STATEMENT HAS USAGES OF CURSOR FOR LOOP **
FOR fUsgClass IN cUsgClass DO
var1 := :temp_fUsgClass_col1;
END FOR;
END;
$$;
Oracle¶
CREATE OR REPLACE PROCEDURE oracle_cursor_for_loop AS
BEGIN
FOR r1 IN (SELECT col1 FROM sample_table) LOOP
NULL;
END LOOP;
END;
CREATE OR REPLACE PROCEDURE oracle_cursor_for_loop ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"oracle"}}'
EXECUTE AS CALLER
AS
$$
BEGIN
LET temporary_for_cursor_0 CURSOR
FOR
(SELECT col1 FROM
sample_table
);
--** SSC-PRF-0004 - THIS STATEMENT HAS USAGES OF CURSOR FOR LOOP **
FOR r1 IN temporary_for_cursor_0 DO
NULL;
END FOR;
END;
$$;
推奨事項¶
その他のサポートについては、サポートチーム snowconvert-support@snowflake.com にお問い合わせください
SSC-PRF-0005¶
注釈
読みやすくするために、この例ではコードの一部を簡略化しています。
重大性¶
なし
説明¶
この警告は、ステートメントにネストされたカーソルが含まれていることを示します。カーソルは、クエリ結果の行を一度に処理するデータベース機能です。ネストされたカーソルは、あるカーソルを別のカーソルのループの中で使用するときに発生します。これはパフォーマンスに影響を与える可能性があり、慎重に評価する必要があります。
ネストされたカーソルは、特に大量のデータを扱う場合、コードのパフォーマンスを著しく低下させます。これは、カーソル操作ごとにデータベースサーバーと通信する必要があり、追加の処理オーバーヘッドと遅延が発生するためです。
コードの例:¶
SQL Server¶
CREATE OR ALTER PROCEDURE procedureSample
AS
BEGIN
DECLARE
@outer_category_id INT,
@outer_category_name NVARCHAR(50),
@inner_product_name NVARCHAR(50);
-- Define the outer cursor
DECLARE outer_cursor CURSOR FOR
SELECT category_id, category_name FROM categories;
-- Open the outer cursor
OPEN @outer_cursor;
-- Fetch the first row from the outer cursor
FETCH NEXT FROM outer_cursor INTO @outer_category_id, @outer_category_name;
-- Start the outer loop
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'Category: ' + @outer_category_name;
-- Define the inner cursor
DECLARE inner_cursor CURSOR FOR
SELECT product_name FROM products WHERE category_id = @outer_category_id;
-- Open the inner cursor
OPEN inner_cursor;
FETCH NEXT FROM inner_cursor INTO @inner_product_name;
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'Product: ' + @inner_product_name + ' Category: ' + CAST(@outer_category_id AS NVARCHAR(10));
-- Fetch the next row from the inner cursor
FETCH NEXT FROM inner_cursor INTO @inner_product_name;
END;
-- Close the inner cursor
CLOSE inner_cursor;
DEALLOCATE inner_cursor;
-- Fetch the next row from the outer cursor
FETCH NEXT FROM outer_cursor INTO @outer_category_id, @outer_category_name;
END;
-- Close the outer cursor
CLOSE outer_cursor;
DEALLOCATE outer_cursor;
END;
CREATE OR REPLACE PROCEDURE procedureSample ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
DECLARE
OUTER_CATEGORY_ID INT;
OUTER_CATEGORY_NAME VARCHAR(50);
INNER_PRODUCT_NAME VARCHAR(50);
-- Define the outer cursor
--** SSC-FDM-TS0013 - SNOWFLAKE SCRIPTING CURSOR ROWS ARE NOT MODIFIABLE **
outer_cursor CURSOR
FOR
SELECT
category_id,
category_name
FROM
categories;
-- Define the inner cursor
--** SSC-FDM-TS0013 - SNOWFLAKE SCRIPTING CURSOR ROWS ARE NOT MODIFIABLE **
inner_cursor CURSOR
FOR
SELECT
product_name
FROM
products
WHERE
category_id = :OUTER_CATEGORY_ID;
BEGIN
-- Open the outer cursor
--** SSC-PRF-0005 - THE STATEMENT BELOW HAS USAGES OF NESTED CURSORS. **
OPEN OUTER_CURSOR;
-- Fetch the first row from the outer cursor
FETCH
outer_cursor
INTO
:OUTER_CATEGORY_ID,
:OUTER_CATEGORY_NAME;
-- Start the outer loop
-- Define the inner cursor
WHILE (:FETCH_STATUS = 0) LOOP
!!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'PRINT' NODE ***/!!!
PRINT 'Category: ' + @outer_category_name;
-- Open the inner cursor
OPEN inner_cursor;
--** SSC-PRF-0003 - FETCH INSIDE A LOOP IS CONSIDERED A COMPLEX PATTERN, THIS COULD DEGRADE SNOWFLAKE PERFORMANCE. **
FETCH
inner_cursor
INTO
:INNER_PRODUCT_NAME;
WHILE (:FETCH_STATUS = 0) LOOP
!!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'PRINT' NODE ***/!!!
PRINT 'Product: ' + @inner_product_name + ' Category: ' + CAST(@outer_category_id AS NVARCHAR(10));
-- Fetch the next row from the inner cursor
--** SSC-PRF-0003 - FETCH INSIDE A LOOP IS CONSIDERED A COMPLEX PATTERN, THIS COULD DEGRADE SNOWFLAKE PERFORMANCE. **
FETCH
inner_cursor
INTO
:INNER_PRODUCT_NAME;
END LOOP;
-- Close the inner cursor
CLOSE inner_cursor;
!!!RESOLVE EWI!!! /*** SSC-EWI-0058 - FUNCTIONALITY FOR 'DEALLOCATE' IS NOT CURRENTLY SUPPORTED BY SNOWFLAKE SCRIPTING ***/!!!
DEALLOCATE inner_cursor;
-- Fetch the next row from the outer cursor
--** SSC-PRF-0003 - FETCH INSIDE A LOOP IS CONSIDERED A COMPLEX PATTERN, THIS COULD DEGRADE SNOWFLAKE PERFORMANCE. **
FETCH
outer_cursor
INTO
:OUTER_CATEGORY_ID,
:OUTER_CATEGORY_NAME;
END LOOP;
-- Close the outer cursor
CLOSE outer_cursor;
!!!RESOLVE EWI!!! /*** SSC-EWI-0058 - FUNCTIONALITY FOR 'DEALLOCATE' IS NOT CURRENTLY SUPPORTED BY SNOWFLAKE SCRIPTING ***/!!!
DEALLOCATE outer_cursor;
END;
$$;
Oracle¶
明示的カーソル¶
CREATE OR REPLACE PROCEDURE procedureSample AS
BEGIN
DECLARE
CURSOR outer_cursor IS
SELECT category_id, category_name FROM categories;
CURSOR inner_cursor (p_category_id NUMBER) IS
SELECT product_name FROM products WHERE category_id = p_category_id;
outer_category_id categories.category_id%TYPE;
outer_category_name categories.category_name%TYPE;
inner_product_name products.product_name%TYPE;
BEGIN
OPEN outer_cursor;
FETCH outer_cursor INTO outer_category_id, outer_category_name;
LOOP
EXIT WHEN outer_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Category: ' || outer_category_name);
OPEN inner_cursor(outer_category_id);
LOOP
FETCH inner_cursor INTO inner_product_name;
EXIT WHEN inner_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Product: ' || inner_product_name || ' Category: ' || outer_category_id);
END LOOP;
CLOSE inner_cursor;
FETCH outer_cursor INTO outer_category_id, outer_category_name;
END LOOP;
CLOSE outer_cursor;
END;
END;
CREATE OR REPLACE PROCEDURE procedureSample ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"oracle"}}'
EXECUTE AS CALLER
AS
$$
BEGIN
DECLARE
outer_cursor CURSOR
FOR
SELECT category_id, category_name FROM
categories;
inner_cursor CURSOR
FOR
SELECT product_name FROM
products
WHERE category_id = ?;
outer_category_id VARIANT !!!RESOLVE EWI!!! /*** SSC-EWI-OR0129 - TYPE ATTRIBUTE 'categories.category_id%TYPE' COULD NOT BE RESOLVED, SO IT WAS TRANSFORMED TO VARIANT ***/!!!;
outer_category_name VARIANT !!!RESOLVE EWI!!! /*** SSC-EWI-OR0129 - TYPE ATTRIBUTE 'categories.category_name%TYPE' COULD NOT BE RESOLVED, SO IT WAS TRANSFORMED TO VARIANT ***/!!!;
inner_product_name VARIANT !!!RESOLVE EWI!!! /*** SSC-EWI-OR0129 - TYPE ATTRIBUTE 'products.PRODUCT_NAME%TYPE' COULD NOT BE RESOLVED, SO IT WAS TRANSFORMED TO VARIANT ***/!!!;
call_results VARIANT;
BEGIN
--** SSC-PRF-0005 - THE STATEMENT BELOW HAS USAGES OF NESTED CURSORS. **
OPEN outer_cursor USING ('DEFAULT VALUE NOT FOUND');
FETCH outer_cursor INTO
:outer_category_id,
:outer_category_name;
LOOP
IF (outer_category_id IS NULL) THEN
EXIT;
END IF;
--** SSC-FDM-OR0035 - CHECK UDF IMPLEMENTATION FOR DBMS_OUTPUT.PUT_LINE_UDF. **
call_results := (
CALL DBMS_OUTPUT.PUT_LINE_UDF('Category: ' || NVL(:outer_category_name :: STRING, ''))
);
OPEN inner_cursor USING (:outer_category_id);
LOOP
--** SSC-PRF-0003 - FETCH INSIDE A LOOP IS CONSIDERED A COMPLEX PATTERN, THIS COULD DEGRADE SNOWFLAKE PERFORMANCE. **
FETCH inner_cursor INTO
:inner_product_name;
IF (inner_product_name IS NULL) THEN
EXIT;
END IF;
--** SSC-FDM-OR0035 - CHECK UDF IMPLEMENTATION FOR DBMS_OUTPUT.PUT_LINE_UDF. **
call_results := (
CALL DBMS_OUTPUT.PUT_LINE_UDF('Product: ' || NVL(:inner_product_name :: STRING, '') || ' Category: ' || NVL(:outer_category_id :: STRING, ''))
);
END LOOP;
CLOSE inner_cursor;
--** SSC-PRF-0003 - FETCH INSIDE A LOOP IS CONSIDERED A COMPLEX PATTERN, THIS COULD DEGRADE SNOWFLAKE PERFORMANCE. **
FETCH outer_cursor INTO
:outer_category_id,
:outer_category_name;
END LOOP;
CLOSE outer_cursor;
RETURN call_results;
END;
END;
$$;
暗黙的カーソル¶
CREATE OR REPLACE PROCEDURE procedureSample AS
BEGIN
DECLARE
inner_category_id categories.category_name%TYPE;
inner_product_name products.product_name%TYPE;
inner_cursor SYS_REFCURSOR;
BEGIN
FOR outer_cursor IN (SELECT category_id, category_name FROM categories)
LOOP
OPEN inner_cursor
FOR SELECT product_name, category_id FROM products WHERE category_id = outer_cursor.category_id;
LOOP
FETCH inner_cursor INTO inner_product_name, inner_category_id;
EXIT WHEN inner_cursor%NOTFOUND;
dbms_output.put_line( 'Category id: '|| outer_cursor.category_id);
dbms_output.put_line('Product name: ' || inner_product_name);
END LOOP;
CLOSE inner_cursor;
END LOOP;
END;
END;
CREATE OR REPLACE PROCEDURE procedureSample ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"oracle"}}'
EXECUTE AS CALLER
AS
$$
BEGIN
DECLARE
inner_category_id VARIANT !!!RESOLVE EWI!!! /*** SSC-EWI-OR0129 - TYPE ATTRIBUTE 'categories.category_name%TYPE' COULD NOT BE RESOLVED, SO IT WAS TRANSFORMED TO VARIANT ***/!!!;
inner_product_name VARIANT !!!RESOLVE EWI!!! /*** SSC-EWI-OR0129 - TYPE ATTRIBUTE 'products.product_name%TYPE' COULD NOT BE RESOLVED, SO IT WAS TRANSFORMED TO VARIANT ***/!!!;
inner_cursor_res RESULTSET;
call_results VARIANT;
BEGIN
LET temporary_for_cursor_0 CURSOR
FOR
(SELECT category_id, category_name FROM
categories
);
--** SSC-PRF-0004 - THIS STATEMENT HAS USAGES OF CURSOR FOR LOOP **
--** SSC-PRF-0005 - THE STATEMENT BELOW HAS USAGES OF NESTED CURSORS. **
FOR outer_cursor IN temporary_for_cursor_0 DO
LET inner_cursor CURSOR
FOR
SELECT product_name, category_id FROM
products
WHERE category_id = outer_cursor.category_id;
OPEN inner_cursor;
LOOP
--** SSC-PRF-0003 - FETCH INSIDE A LOOP IS CONSIDERED A COMPLEX PATTERN, THIS COULD DEGRADE SNOWFLAKE PERFORMANCE. **
FETCH inner_cursor INTO
:inner_product_name,
:inner_category_id;
IF (inner_product_name IS NULL) THEN
EXIT;
END IF;
--** SSC-FDM-OR0035 - CHECK UDF IMPLEMENTATION FOR DBMS_OUTPUT.PUT_LINE_UDF. **
call_results := (
CALL dbms_output.put_line( 'Category id: ' || NVL(outer_cursor.category_id :: STRING, ''))
);
--** SSC-FDM-OR0035 - CHECK UDF IMPLEMENTATION FOR DBMS_OUTPUT.PUT_LINE_UDF. **
call_results := (
CALL dbms_output.put_line('Product name: ' || NVL(:inner_product_name :: STRING, ''))
);
END LOOP;
CLOSE inner_cursor;
END FOR;
RETURN call_results;
END;
END;
$$;
推奨事項¶
ネストされたカーソルはパフォーマンスに悪影響を及ぼし、コードを複雑にするため避けるようにします。
ネストされたカーソルの代わりに、次のような SQL を使用します。
SQL 関数
結合
サブクエリ
ウィンドウ関数
共通テーブル式(CTEs)
再帰クエリ。これらの方法は、大量のデータを処理するのに適しています。
その他のサポートについては、 snowconvert-support@snowflake.com にお問い合わせください
SSC-PRF-0001¶
説明¶
この警告は、ステートメントがカーソルフェッチ一括操作を使用していることを示しています。これらの操作により、一度に1行ずつではなく、一度に複数行のデータをカーソルから取得することができます。一括操作を使用すると、クライアントとサーバー間の通信回数を減らすことができ、パフォーマンスが向上します。
このパターンは正しく実装しないと複雑になります。たとえば、1回のフェッチ操作で多くの行を取得すると、メモリ使用量が多くなります。フェッチする行数と利用可能なシステムメモリとのバランスを慎重に検討することが不可欠です。
コード例¶
Oracle¶
CREATE OR REPLACE PROCEDURE oracle_cursor_fetch_bulk AS
--cursor and variable declarations
BEGIN
OPEN c1;
FETCH c1 BULK COLLECT INTO col1;
CLOSE c1;
END;
CREATE OR REPLACE PROCEDURE oracle_cursor_fetch_bulk ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"oracle"}}'
EXECUTE AS CALLER
AS
--cursor and variable declarations
$$
BEGIN
OPEN c1;
--** SSC-PRF-0001 - THIS STATEMENT HAS USAGES OF CURSOR FETCH BULK OPERATIONS **
c1 := (
CALL FETCH_BULK_COLLECTION_RECORDS_UDF(:c1)
);
col1 := :c1:RESULT;
CLOSE c1;
END;
$$;
推奨事項¶
その他のサポートについては、 snowconvert-support@snowflake.com にお問い合わせください
SSC-PRF-TS0001¶
説明¶
この警告は、 SnowConvert が共通テーブル式(CTE)を検出したものの、 CTE のクエリ定義に再帰操作が含まれているかどうかを検証していない場合に表示されます。
Snowflake SQL では、再帰的な共通テーブル式(CTE)クエリには RECURSIVE キーワードが必要です。現在、 SnowConvert は、 RECURSIVE キーワードを含めるべきかどうかを判断するために、再帰クエリを自動的に検出しません。この警告は、適切な場所に RECURSIVE キーワードを手動で追加する必要があることを通知します。
この検証のサポートは、要件の進化に合わせて将来のリリースで追加される予定です。
コード例¶
入力コード:¶
WITH Sales_CTE (SalesPersonID, NumberOfOrders)
AS
(
SELECT SalesPersonID, 2
FROM Sales.SalesOrderHeader
WHERE SalesPersonID IS NOT NULL
GROUP BY SalesPersonID
)
SELECT 2 AS "Average Sales Per Person"
FROM Sales_CTE;
出力コード:¶
--** SSC-PRF-TS0001 - PERFORMANCE WARNING - RECURSION FOR CTE NOT CHECKED. MIGHT REQUIRE RECURSIVE KEYWORD **
WITH Sales_CTE (
SalesPersonID,
NumberOfOrders
) AS
(
SELECT
SalesPersonID, 2
FROM
Sales.SalesOrderHeader
WHERE
SalesPersonID IS NOT NULL
GROUP BY
SalesPersonID
)
SELECT 2 AS "Average Sales Per Person"
FROM
Sales_CTE;
推奨事項¶
RECURSIVE キーワードはオプションで、クエリ結果には影響しません。しかし、実行中にSnowflakeがリソースを割り当てる方法に影響を与える可能性があります。Snowflakeの CTE ドキュメントをご覧になり、互換性のある CTE クエリに RECURSIVE キーワードの自動追加をご希望の場合は、弊社までご連絡いただくことをお勧めします。
その他のサポートについては、 snowconvert-support@snowflake.com にお問い合わせください