SnowConvert: パフォーマンスレビューのメッセージ¶
パフォーマンスレビュー(PRF)の問題は、 SnowConvert がソースコードを有効なSnowflake構文に正常に翻訳したものの、結果として生成されたコードがSnowflakeで最適なパフォーマンスを発揮しない可能性があることを示しています。変換後のコードで PRF の問題が発生した場合は、その部分を注意深く見直し、パフォーマンスを向上させるために書き換えが可能かどうかを検討することをお勧めします。
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-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"}}'
;
Microsoft 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
ステートメントは、結果セットから個々の行を一度に取得し、処理します。
ループ内でカーソルを使用する大容量データセットの処理は、特に以下のような場合に複雑になります。
複数のテーブルの結合が関係する場合
複雑な計算が必要な場合
大量の行の処理が必要な場合
このパターンはパフォーマンスの問題につながる可能性があり、データ量が増えるにつれて維持するのが難しくなります。
コード例¶
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 句を使用して複数の行を同時に操作します。このアプローチはより効率的で、メンテナンスも簡単です。
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-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-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 関数
結合
サブクエリ
ウィンドウ関数
共通テーブル式(CTEs)
再帰クエリ。これらのオプションは、大量のデータを効率的に処理するのに適しています。
その他のサポートについては、 snowconvert-support@snowflake.com にお問い合わせください
SSC-PRF-TS0001¶
説明¶
この警告は、 SnowConvert が共通テーブル式(CTE)を検出したものの、 CTE のクエリ定義に再帰操作が含まれているかどうかを検証していない場合に表示されます。
Snowflake SQL では、再帰的な共通テーブル式(CTEs)には RECURSIVE キーワードが必要です。現在、 SnowConvert は、 RECURSIVE キーワードを含めるべきかどうかを判断するために、再帰クエリを自動的に検出しません。この警告は、再帰的な CTEs のために 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がリソースを割り当てる方法に影響を与える可能性があります。互換性のある CTE クエリに対して RECURSIVE キーワードの自動追加をご希望の場合は、Snowflakeの CTE ドキュメントをご確認の上、弊社までご連絡いただくことをお勧めします。
その他のサポートについては、 snowconvert-support@snowflake.com にメールでご連絡ください