SnowConvert : messages d’examen des performances

Un problème d’examen des performances (PRF) survient lorsque SnowConvert traduit avec succès le code source vers Snowflake, mais que le code résultant peut ne pas être optimisé pour l’environnement de Snowflake. Lorsque vous rencontrez un PRF dans votre code converti, nous vous recommandons d’examiner attentivement cette section et de voir si vous pouvez la réécrire pour améliorer les performances.

SSC-PRF-0006

Gravité

Aucun(e)

Description

Ce message apparaît lorsqu’une requête contient une définition de curseur. Lorsqu’une expression de curseur est évaluée, elle renvoie et ouvre automatiquement un curseur imbriqué. Pour en savoir plus sur les expressions de curseur, voir Oracle Cursor Expression.

Exemples de code :

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;
Copy
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;
Copy

Recommandations

  • Les curseurs imbriqués doivent être évités car ils peuvent nuire aux performances et rendre le code plus complexe.

  • Au lieu de curseurs imbriqués, utilisez d’autres fonctions de SQL telles que :

    • Fonctions SQL

    • Jointures

    • Sous-requêtes

    • Fonctions de fenêtre

    • Expressions de table communes (CTEs)

    • Requêtes récursives Ces alternatives sont plus adaptées au traitement de grandes quantités de données.

  • Pour toute assistance supplémentaire, contactez-nous à l’adresse suivante : snowconvert-support@snowflake.com

SSC-PRF-0002

Description

L’utilisation du classement dans Snowflake peut avoir un impact sur les performances des requêtes, en particulier lorsqu’il est utilisé dans les clauses WHERE. Pour en savoir plus sur la manière dont le classement affecte les performances, veuillez vous référer à la documentation sur les conséquences de l’utilisation du classement sur les performances.

Un avertissement s’affiche lorsqu’une colonne est créée avec un classement insensible à la casse. L’utilisation de cette colonne dans les requêtes peut ralentir les performances.

Exemples de code :

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"}}'
;
Copy

Oracle

CREATE TABLE exampleTable (
    col1 VARCHAR(50) COLLATE BINARY_CI,
    col2 VARCHAR(50) COLLATE BINARY_CS
);
Copy
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"}}'
   ;
Copy

SQL Server

CREATE TABLE exampleTable (
    col1 VARCHAR(50) COLLATE Latin1_General_CI_AS,
    col2 VARCHAR(50) COLLATE Latin1_General_CS_AS
);
Copy
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"}}'
;
Copy

Recommandations

  • Évaluez l’impact du classement insensible à la casse sur les performances de votre application. Si l’impact est important, envisagez de modifier votre code pour éviter de l’utiliser. Si l’impact sur les performances est minime, vous pouvez ignorer cet avertissement.

  • Pour toute assistance supplémentaire, contactez-nous à l’adresse suivante : snowconvert-support@snowflake.com

SSC-PRF-0003

Gravité

Faible

Description

Cet avertissement apparaît lorsqu’une instruction FETCH est détectée dans une boucle. L’instruction FETCH récupère des lignes individuelles d’un jeu de résultats, ce qui vous permet de traiter chaque ligne une par une.

Le traitement de grands ensembles de données à l’aide de curseurs dans des boucles peut devenir complexe, en particulier lorsque :

  • Des jointures de tables multiples sont impliquées

  • Des calculs complexes sont nécessaires

  • Un grand nombre de lignes doit être traité

Cette approche peut entraîner des problèmes de performance et peut être difficile à maintenir au fur et à mesure que les volumes de données augmentent.

Exemple de code

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;
Copy
--** 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;
$$;
Copy

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;
Copy
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;
$$;
Copy

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;
Copy
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;
$$;
Copy

Recommandations

Pour éviter les modèles complexes, utilisez des opérations basées sur des ensembles plutôt que des boucles. Écrivez les instructions SQL (SELECT, UPDATE, DELETE) avec les clauses WHERE pour traiter plusieurs lignes à la fois, plutôt que de traiter les lignes une par une.

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;
Copy
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;
$$;
Copy

Les opérations basées sur les ensembles peuvent être utilisées pour traiter les données plus efficacement.

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;
Copy

Les opérations basées sur les ensembles peuvent être utilisées pour manipuler les données.

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;
$$;
Copy

Recommandations

SSC-PRF-0007

Description

Identifie les problèmes de performance qui peuvent survenir lors de l’utilisation de la commande CLUSTER BY.

Exemple de code

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
Copy

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
Copy

Transigez :

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)
);
Copy

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" }}'
;
Copy

Recommandations

  • Examinez votre code pour identifier les goulets d’étranglement potentiels en matière de performance. Pour plus de détails sur l’optimisation des performances, veuillez vous référer à cette documentation.

  • Pour toute assistance supplémentaire, veuillez contacter notre équipe de support à l’adresse suivante : snowconvert-support@snowflake.com

SSC-PRF-0004

Gravité

Aucun(e)

Description

Cet avertissement indique que l’instruction contient un curseur pour des opérations en boucle. Une boucle de curseur est une structure de programmation qui traite les résultats d’une requête une ligne à la fois, ce qui vous permet de travailler avec des enregistrements individuels d’un jeu de résultats.

Cet avertissement permet d’identifier les problèmes de performance potentiels dans les boucles FOR du curseur. Des problèmes de performance peuvent survenir dans les cas suivants :

  • L’instruction SELECT au sein du curseur renvoie un grand ensemble de données

  • La boucle contient des opérations complexes

  • La boucle contient des boucles imbriquées

Bien que SnowConvert puisse détecter ces modèles, vous devez revoir et optimiser le code pour garantir une exécution efficace.

Exemple de code

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;
Copy
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;
$$;
Copy

Oracle

CREATE OR REPLACE PROCEDURE oracle_cursor_for_loop AS
BEGIN
    FOR r1 IN (SELECT col1 FROM sample_table) LOOP
        NULL;
    END LOOP;
END;
Copy
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;
$$;
Copy

Recommandations

SSC-PRF-0005

Note

Pour une meilleure lisibilité, nous avons simplifié certaines sections du code dans cet exemple.

Gravité

Aucun(e)

Description

Cet avertissement indique que l’instruction contient des curseurs imbriqués. Un curseur est une fonction de base de données qui vous permet de traiter les lignes d’un résultat de requête une par une. On parle de curseurs imbriqués lorsque vous utilisez un curseur à l’intérieur de la boucle d’un autre curseur, ce qui peut avoir un impact sur les performances et doit être soigneusement évalué.

Les curseurs imbriqués peuvent ralentir considérablement les performances de votre code, en particulier lorsque vous travaillez avec de grandes quantités de données. En effet, à chaque opération, le curseur doit communiquer avec le serveur de la base de données, ce qui entraîne des frais généraux et des retards supplémentaires.

Exemples de code :

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;
Copy
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;
$$;
Copy

Oracle

Curseur explicite

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;
Copy
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;
$$;
Copy

Curseur implicite

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;
Copy
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;
$$;
Copy

Recommandations

  • Les curseurs imbriqués doivent être évités car ils peuvent nuire aux performances et rendre le code plus complexe.

  • Au lieu de curseurs imbriqués, utilisez les alternatives SQL telles que :

    • Fonctions SQL

    • Jointures

    • Sous-requêtes

    • Fonctions de fenêtre

    • Expressions de table communes (CTEs)

    • Requêtes récursives Ces méthodes sont plus adaptées au traitement de grandes quantités de données.

  • Pour toute assistance supplémentaire, contactez-nous à l’adresse suivante : snowconvert-support@snowflake.com

SSC-PRF-0001

Description

Cet avertissement indique que l’instruction utilise des opérations d’extraction en bloc du curseur. Ces opérations vous permettent de récupérer plusieurs lignes de données d’un curseur en même temps, au lieu d’une ligne à la fois. L’utilisation d’opérations en bloc améliore les performances en réduisant le nombre de communications nécessaires entre le client et le serveur.

Ce modèle peut devenir complexe s’il n’est pas mis en œuvre correctement. Par exemple, récupérer un trop grand nombre de lignes en une seule opération peut entraîner une utilisation importante de la mémoire. Il est essentiel d’examiner attentivement l’équilibre entre le nombre de lignes extraites et la mémoire disponible dans le système.

Exemple de code

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;
Copy
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;
$$;
Copy

Recommandations

SSC-PRF-TS0001

Description

Cet avertissement apparaît lorsque SnowConvert détecte une expression de table commune (CTE) mais n’a pas vérifié si CTE contient des opérations récursives dans sa définition de requête.

Snowflake SQL requiert le mot-clé RECURSIVE pour les requêtes récursives de type Common Table Expression (CTE). Actuellement, SnowConvert ne détecte pas automatiquement les requêtes récursives pour déterminer si le mot-clé RECURSIVE doit être inclus. Cet avertissement vous signale que vous devrez peut-être ajouter manuellement le mot-clé RECURSIVE le cas échéant.

La prise en charge de cette validation sera ajoutée dans les versions futures en fonction de l’évolution des exigences.

Exemple de code

Code d’entrée :

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;
Copy

Code de sortie :

--** 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;
Copy

Recommandations

  • Le mot-clé RECURSIVE est facultatif et n’affectera pas les résultats de votre requête. Cependant, il peut influencer la manière dont Snowflake alloue les ressources pendant l’exécution. Nous vous recommandons de consulter la documentation de Snowflake sur CTE et de nous contacter si vous souhaitez l’ajout automatique du mot-clé RECURSIVE pour des requêtes compatibles avec CTE.

  • Pour toute assistance supplémentaire, veuillez nous contacter à l’adresse suivante : snowconvert-support@snowflake.com