SnowConvert AI - 일반 성능 검토 메시지¶
SSC-PRF-0001¶
이 문에서 커서 가져오기 대량 작업이 사용됩니다
설명¶
이 경고는 문이 커서 가져오기 대량 작업을 사용한다는 것을 나타냅니다. 이러한 작업을 사용하면 커서에서 한 번에 한 행씩이 아니라 여러 행의 데이터를 한 번에 검색할 수 있습니다. 대량 작업을 사용하면 클라이언트와 서버 간에 필요한 통신 횟수가 줄어들어 성능이 향상됩니다.
이 패턴은 올바르게 구현하지 않으면 복잡해질 수 있습니다. 예를 들어, 한 번의 가져오기 작업에서 너무 많은 행을 검색하면 메모리가 과도하게 소모될 수 있습니다. 가져오는 행 수와 사용 가능한 메모리 리소스 간의 균형을 유지하는 것이 중요합니다.
코드 예제¶
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 서버¶
입력¶
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¶
루프 내부의 가져오기는 복잡한 패턴으로 간주되므로 Snowflake 성능을 저하시킬 수 있습니다
심각도¶
낮음
설명¶
이 경고는 루프 내에서 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/02/2025", "domain": "no-domain-provided" }}'
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": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "oracle", "convertedOn": "07/02/2025", "domain": "no-domain-provided" }}'
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 ***/!!!;
--** SSC-PRF-0009 - PERFORMANCE REVIEW - CURSOR USAGE **
cursor1 CURSOR
FOR
SELECT COLUMN_NAME FROM
table1;
BEGIN
WHILE (true)
--** SSC-PRF-0008 - PERFORMANCE REVIEW - LOOP USAGE **
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 서버¶
입력¶
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;
$$;
모범 사례¶
성능을 개선하고 복잡한 패턴을 피하려면 루프 대신 세트 기반 작업을 사용하십시오. 행 단위 처리를 WHERE 절을 사용하여 여러 행에서 동시에 작업하는 SQL 문(SELECT, UPDATE, DELETE)로 바꿉니다. 이 접근법이 더 효율적이고 유지 관리가 쉽습니다.
Oracle¶
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;
Snowflake¶
CREATE OR REPLACE PROCEDURE cursor_fetch_inside_loop ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "oracle", "convertedOn": "07/02/2025", "domain": "no-domain-provided" }}'
EXECUTE AS CALLER
AS
$$
DECLARE
record_employee OBJECT !!!RESOLVE EWI!!! /*** SSC-EWI-0036 - ROWTYPE DATA TYPE CONVERTED TO OBJECT ***/!!! := OBJECT_CONSTRUCT();
--** SSC-PRF-0009 - PERFORMANCE REVIEW - CURSOR USAGE **
emp_cursor CURSOR
FOR
SELECT
OBJECT_CONSTRUCT( *) sc_cursor_record FROM
employees;
BEGIN
OPEN emp_cursor;
--** SSC-PRF-0008 - PERFORMANCE REVIEW - LOOP USAGE **
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¶
이 문에서 cursor for loop가 사용됩니다
심각도¶
없음
설명¶
이 경고는 문에 루프용 커서가 포함되어 있음을 나타냅니다. 루프용 커서는 쿼리 결과를 한 번에 한 행씩 처리하는 프로그래밍 구조로, 결과 세트의 개별 레코드로 작업할 수 있습니다.
이 경고는 커서 FOR 루프의 잠재적인 성능 문제를 식별하는 데 도움이 됩니다. 성능 문제가 발생할 수 있습니다.
커서 내에 있는 SELECT 문은 큰 데이터 세트를 반환합니다
루프에는 복잡한 작업이 포함되어 있습니다
루프에는 중첩된 루프가 포함됩니다
SnowConvert AI는 이러한 패턴을 감지할 수 있지만 효율적인 실행을 보장하기 위해 코드를 검토하고 최적화해야 합니다.
코드 예제¶
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;
출력¶
--** SSC-FDM-0007 - MISSING DEPENDENT OBJECT "sample_table" **
CREATE OR REPLACE PROCEDURE teradata_cursor_for_loop ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "teradata", "convertedOn": "07/16/2025", "domain": "no-domain-provided" }}'
EXECUTE AS CALLER
AS
$$
DECLARE
!!!RESOLVE EWI!!! /*** SSC-EWI-0110 - TRANSFORMATION NOT PERFORMED DUE TO MISSING DEPENDENCIES ***/!!!
temp_fUsgClass_col1;
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
temp_fUsgClass_col1 := fUsgClass.col1;
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¶
아래 문에서 중첩 커서가 사용됩니다
참고
가독성을 높이기 위해 이 예제에서는 코드의 일부 섹션을 간소화했습니다.
심각도¶
없음
설명¶
이 경고는 문에 중첩된 커서가 포함되어 있음을 나타냅니다. 커서는 쿼리 결과의 행을 한 번에 1개씩 처리할 수 있는 데이터베이스 기능입니다. 중첩 커서는 한 커서를 다른 커서의 루프 안에 사용할 때 발생하며, 성능에 영향을 줄 수 있으므로 주의해서 사용해야 합니다.
중첩된 커서는 특히 많은 양의 데이터로 작업할 때 코드의 성능을 크게 저하시킬 수 있습니다. 커서가 작업할 때마다 데이터베이스 서버와 통신해야 하므로 추가적인 처리 오버헤드와 지연이 발생하기 때문입니다.
코드 예¶
SQL 서버¶
입력¶
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": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "oracle", "convertedOn": "07/02/2025", "domain": "no-domain-provided" }}'
EXECUTE AS CALLER
AS
$$
BEGIN
DECLARE
--** SSC-PRF-0009 - PERFORMANCE REVIEW - CURSOR USAGE **
outer_cursor CURSOR
FOR
SELECT category_id, category_name FROM
categories;
--** SSC-PRF-0009 - PERFORMANCE REVIEW - CURSOR USAGE **
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;
--** SSC-PRF-0008 - PERFORMANCE REVIEW - LOOP USAGE **
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);
--** SSC-PRF-0008 - PERFORMANCE REVIEW - LOOP USAGE **
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": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "oracle", "convertedOn": "07/02/2025", "domain": "no-domain-provided" }}'
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;
--** SSC-PRF-0008 - PERFORMANCE REVIEW - LOOP USAGE **
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¶
쿼리 내부의 중첩 커서는 Snowflake에서 지원되지 않습니다
심각도¶
없음
설명¶
쿼리에 커서 정의가 포함된 경우 이 메시지가 표시됩니다. 커서 식이 평가되면 반환되고 중첩된 커서가 자동으로 열립니다. 자세한 내용은 Oracle Cursor 식 을 참조하십시오.
코드 예¶
입력¶
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-0007¶
PERFORMANCE REVIEW - CLUSTER BY
설명¶
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),
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": "07/02/2025", "domain": "no-domain-provided" }}'
; -- 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-0010¶
Partition by removed, at least one of the specified expressions have no iceberg partition transform equivalent
심각도¶
없음
설명¶
Snowflake supports the PARTITION BY clause in Iceberg tables, however, only Iceberg partition transforms are supported. When transforming paritioning into Iceberg tables, SnowConvert AI will generate the equivalent partition transforms for supported cases. When no partition transform equivalent can be generated for the partition expressions, the PARTITION BY will be removed from the table by commenting it out with this PRF.
This PRF is only generated when SnowConvert AI migrates tables into Iceberg tables using the Tables translation conversion setting.
코드 예¶
입력¶
-- Additional Params: --TablesTransformationTarget SnowflakeIceberg
CREATE TABLE FINANCE.FINANCE_TABLE
(
customerName VARCHAR(30),
accountBalance VARCHAR(20)
)
PARTITION BY CASE_N(
accountBalance < 0 ,
accountBalance >= 0);
출력¶
-- Additional Params: --TablesTransformationTarget SnowflakeIceberg
CREATE OR REPLACE ICEBERG TABLE FINANCE.FINANCE_TABLE
(
customerName VARCHAR,
accountBalance VARCHAR
)
CATALOG = 'SNOWFLAKE'
-- --** SSC-PRF-0010 - PARTITION BY REMOVED, AT LEAST ONE OF THE SPECIFIED EXPRESSIONS HAVE NO ICEBERG PARTITION TRANSFORM EQUIVALENT **
-- PARTITION BY CASE_N(
-- accountBalance < 0 ,
-- accountBalance >= 0)
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 1, "minor": 0, "patch": "0.0" }, "attributes": { "component": "teradata", "convertedOn": "12/16/2025", "domain": "no-domain-provided", "migrationid": "9CebAVkM33qsfTnTrMh3Dw==" }}'
;
모범 사례¶
Analyze the impact of partitioning in the performance of queries over the generated Iceberg tables, if the difference is neglible then this PRF can be safely ignored.
추가 지원이 필요하면 snowconvert-support@Snowflake.com으로 문의해 주십시오.