This is a translation reference to convert the Oracle Record Declaration to Snowflake
This section is a work in progress, information may change in the future.
Description
A record variable is a composite variable whose internal components, called fields, can have different data types. The value of a record variable and the values of its fields can change.
You reference an entire record variable by its name. You reference a record field with the syntax record.field.
You can create a record variable in any of these ways:
Define a record type and then declare a variable of that type.
Use %ROWTYPE to declare a record variable that represents either a full or partial row of a database table or view.
Use %TYPE to declare a record variable of the same type as a previously declared record variable.
For the translation, the type definition is replaced by an OBJECT Semi-structured Data Type and then its usages are changed accordingly across any operations.
In order to define a Record type, the syntax is as follows:
This sample attempts to insert two new rows using a record variable which is reassigned mid-procedure.
Oracle
CREATETABLErecord_table(col1 FLOAT, col2 INTEGER);CREATEORREPLACEPROCEDURE record_procedureISTYPE record_typ IS RECORD(col1 INTEGER, col2 FLOAT); record_variable record_typ := record_typ(1, 1.5);--initializationBEGININSERT INTO record_table(col1, col2)VALUES (record_variable.col2, record_variable.col1);--usage--reassignment of properties record_variable.col1 :=2; record_variable.col2 :=2.5;INSERT INTO record_table(col1, col2)VALUES (record_variable.col2, record_variable.col1);--usageEND;CALL record_procedure();SELECT*FROM record_table;
|COL1|COL2|
|----+----|
| 1.5| 1|
| 2.5| 2|
Snowflake
Notice how the reassignments are replaced by an OBJECT_INSERT that updates if the column already exists, and how the VALUES clause is replaced by a SELECT.
CREATE OR REPLACETABLEPUBLIC.record_table ( col1 FLOAT, col2 INTEGER);CREATEORREPLACEPROCEDURE PUBLIC.record_procedure ()RETURNSVARCHARLANGUAGESQLEXECUTEASCALLERAS$$DECLARE-- ** MSC-WARNING - MSCEWI1056 - CUSTOM TYPES ARE NOT SUPPORTED IN SNOWFLAKE BUT REFERENCES TO THIS CUSTOM TYPE WERE CHANGED TO OBJECT **
-- TYPE record_typ IS RECORD(col1 INTEGER, col2 FLOAT) ; record_variable OBJECT := OBJECT_CONSTRUCT('col1', 1, 'col2', 1.5);--initialization--initializationBEGININSERT INTO PUBLIC.record_table(col1, col2)SELECT :record_variable:col2, :record_variable:col1;--usage--reassignment of properties record_variable := OBJECT_INSERT(record_variable, 'col1', 2, true); record_variable := OBJECT_INSERT(record_variable, 'col2', 2.5, true);INSERT INTO PUBLIC.record_table(col1, col2)SELECT :record_variable:col2, :record_variable:col1;--usageEND;$$;CALL PUBLIC.record_procedure();SELECT*FROM PUBLIC.record_table;
COL1|COL2|
----+----+
1.5| 1|
2.5| 2|
%ROWTYPE Record and Values Record
Since the operations are the ones that define the structure, these definitions can be replaced by an OBJECT datatype, but the values of the record need to be decomposed as inserting the record "as-is" is not supported.
Please note the additional OBJECT_CONSTRUCT in the Cursor definition, this is what allows to extract an OBJECT, which then can be used to seamlessly migrate the FETCH statement.
CREATE OR REPLACETABLErecord_table(col1 INTEGER, col2 VARCHAR2(50), col3 DATE);INSERT INTO PUBLIC.record_table(col1, col2 , col3) VALUES (1, 'Hello', DATE'2020-12-25');CREATEORREPLACEPROCEDURE PUBLIC.load_cursor_record ()RETURNSVARCHARLANGUAGESQLEXECUTEASCALLERAS$$DECLARE record_cursor CURSORFORSELECT OBJECT_CONSTRUCT(*)FROM PUBLIC.record_table; record_variable OBJECT;BEGINOPEN record_cursor;LOOPFETCH record_cursor INTO record_variable;IF (record_variable ISNULL) THEN EXIT ;ENDIF;CALL DBMS_OUTPUT.PUT_LINE(:record_variable:COL1);--columns are case_sensitiveCALL DBMS_OUTPUT.PUT_LINE(:record_variable:COL2);CALL DBMS_OUTPUT.PUT_LINE(:record_variable:COL3);ENDLOOP;CLOSE record_cursor;END;$$;CALL PUBLIC.load_cursor_record();SELECT*FROM DBMS_OUTPUT.DBMS_OUTPUT_LOG;
DBMS OUTPUT
-----------
1
Hello
25-DEC-20
Assigning a Record Variable in a SELECT INTO
This transformation consists in taking advantage of the OBJECT_CONTRUCT function to initialize the record using the SELECT columns as the arguments.
Sample auxiliary code
createtablesample_table(ID number, NAMEvarchar2(23));CREATETABLERESULTS (COL1 VARCHAR(20), COL2 VARCHAR(40));insert into sample_table values(1, 'NAME 1');insert into sample_table values(2, 'NAME 2');insert into sample_table values(3, 'NAME 3');insert into sample_table values(4, 'NAME 4');
CREATE OR REPLACETABLEPUBLIC.sample_table ( ID NUMBER(38, 18) /*** MSC-WARNING - MSCEWI1066 - NUMBER TYPE COLUMN MAY NOT BEHAVE SIMILARLY IN SNOWFLAKE ***/,NAMEVARCHAR(23) /*** MSC-WARNING - MSCEWI1036 - VARCHAR2 DATA TYPE CONVERTED TO VARCHAR ***/);CREATE OR REPLACETABLEPUBLIC.RESULTS ( COL1 VARCHAR(20), COL2 VARCHAR(40));insert into PUBLIC.sample_tablevalues(1, 'NAME 1');insert into PUBLIC.sample_tablevalues(2, 'NAME 2');insert into PUBLIC.sample_tablevalues(3, 'NAME 3');insert into PUBLIC.sample_tablevalues(4, 'NAME 4');insert into PUBLIC.sample_tablevalues(5, 'NAME 5');insert into PUBLIC.sample_tablevalues(6, 'NAME 6');
Oracle
CREATEORREPLACEPROCEDURE sp_sample1 AS-- Rowtype variablerowtype_variable sample_table%rowtype;--Record variableTYPE record_typ_def IS RECORD(ID number, NAMEvarchar2(23));record_variable_def record_typ_def;-- Auxiliary variablename_var VARCHAR(20);BEGINSELECT*INTO rowtype_variable FROM sample_table WHERE ID =1FETCHNEXT1ROWS ONLY; name_var := rowtype_variable.NAME;INSERT INTO RESULTS(COL1, COL2) VALUES('SELECT 1', name_var);SELECT ID, NAMEINTO rowtype_variable FROM sample_table WHERE ID =2FETCHNEXT1ROWS ONLY; name_var := rowtype_variable.NAME;INSERT INTO RESULTS(COL1, COL2) VALUES('SELECT 2', name_var);SELECT*INTO record_variable_def FROM sample_table WHERE ID =3FETCHNEXT1ROWS ONLY; name_var := record_variable_def.NAME;INSERT INTO RESULTS(COL1, COL2) VALUES('SELECT 3', name_var);SELECT ID, NAMEINTO record_variable_def FROM sample_table WHERE ID =4FETCHNEXT1ROWS ONLY; name_var := record_variable_def.NAME;INSERT INTO RESULTS(COL1, COL2) VALUES('SELECT 4', name_var);END;call sp_sample1();SELECT*FROM results;
CREATEORREPLACEPROCEDURE PUBLIC.sp_sample1 ()RETURNSVARCHARLANGUAGESQLEXECUTEASCALLERAS$$DECLARE-- Rowtype variable rowtype_variable OBJECT /*** MSC-WARNING - MSCEWI1036 - ROWTYPE DATA TYPE CONVERTED TO OBJECT ***/ := OBJECT_CONSTRUCT();
-- ** MSC-WARNING - MSCEWI1056 - CUSTOM TYPES ARE NOT SUPPORTED IN SNOWFLAKE BUT REFERENCES TO THIS CUSTOM TYPE WERE CHANGED TO OBJECT **
-- TYPE record_typ_def IS RECORD(ID NUMBER(38, 18), NAME VARCHAR(23) /*** MSC-WARNING - MSCEWI1036 - VARCHAR2 DATA TYPE CONVERTED TO VARCHAR ***/)
;
--Record variable record_variable_def OBJECT := OBJECT_CONSTRUCT() /*** MSC-WARNING - MSCEWI1036 - record_typ_def DATA TYPE CONVERTED TO OBJECT ***/;
-- Auxiliary variable name_var VARCHAR(20);BEGINSELECT OBJECT_CONSTRUCT( *) INTO rowtype_variable FROM PUBLIC.sample_tableWHERE ID =1FETCHNEXT1ROWS ONLY; name_var := :rowtype_variable:NAME;INSERT INTO PUBLIC.RESULTS(COL1, COL2) VALUES('SELECT 1', :name_var);SELECT OBJECT_CONSTRUCT('ID', ID, 'NAME', NAME) INTO rowtype_variable FROM PUBLIC.sample_tableWHERE ID =2FETCHNEXT1ROWS ONLY; name_var := :rowtype_variable:NAME;INSERT INTO PUBLIC.RESULTS(COL1, COL2) VALUES('SELECT 2', :name_var);SELECT OBJECT_CONSTRUCT( *) INTO :record_variable_defFROM PUBLIC.sample_tableWHERE ID =3FETCHNEXT1ROWS ONLY; name_var := :record_variable_def:NAME;INSERT INTO PUBLIC.RESULTS(COL1, COL2) VALUES('SELECT 3', :name_var);SELECT OBJECT_CONSTRUCT('ID', ID, 'NAME', NAME) INTO :record_variable_defFROM PUBLIC.sample_tableWHERE ID =4FETCHNEXT1ROWS ONLY; name_var := :record_variable_def:NAME;INSERT INTO PUBLIC.RESULTS(COL1, COL2) VALUES('SELECT 4', :name_var);END;$$;CALL PUBLIC.sp_sample1();SELECT*FROM PUBLIC.results;