SnowConvert AI - SQL Server-Azure Synapse - 프로시저¶
이 섹션에서는 구문 및 프로시저의 TSQL 문을 Snowflake Javascript로 변환하는 작업을 설명합니다
Applies to
SQL 서버
Azure 시냅스 분석
참고
출력 코드의 일부 부분은 명확성을 위해 생략되었습니다.
1. CREATE PROCEDURE Translation¶
Snowflake CREATE PROCEDURE 는 SQL 구문에서 정의되는 반면 내부 문은 JavaScript 에 정의됩니다.
Transact¶
-- Additional Params: -t JavaScript
CREATE PROCEDURE HumanResources.uspGetAllEmployees
@FirstName NVARCHAR(50),
@Age INT
AS
-- TSQL Statements and queries...
GO
Snowflake¶
CREATE OR REPLACE PROCEDURE HumanResources.uspGetAllEmployees (FIRSTNAME STRING, AGE INT)
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
$$;
매개 변수의 DATA TYPE¶
매개 변수 데이터 타입은 Snowflake에 해당하는 데이터 타입으로 변환됩니다. 데이터 타입도 참조하세요.
EXEC 헬퍼¶
SnowFlake 환경에서 프로시저에서 문을 실행하려면 이러한 문을 사전 처리하고 소스 언어에 특정한 여러 변수에 실행이 반영되도록 조정해야 합니다.
SnowConvert AI는 지원되는 문을 자동으로 변환하고 EXEC 헬퍼를 사용합니다. 이 헬퍼는 이러한 문이 네이티브 환경에서 실행되는 방식을 시뮬레이션하는 많은 변수를 액세스 및 업데이트하는 기능을 제공합니다.
예를 들어, 마이그레이션된 프로시저에서 항상 추가되는 코드 블록이 있는 것을 볼 수 있습니다. 다음 섹션에서 이 코드의 기본 구조에 대해 설명하겠습니다. 저희는 항상 변형을 간소화할 수 있는 새롭고 개선된 방법과 요구 사항이 있는 헬퍼를 평가하고 찾고 있다는 점을 명심하십시오.
구조¶
EXEC 헬퍼의 기본 구조는 다음과 같습니다.
변수 선언 섹션: 프로시저 내부에서 문의 실행과 관련된 값을 담을 다양한 변수나 오브젝트를 선언합니다. 여기에는 문의 영향을 받는 행 수 또는 결과 세트 자체와 같은 값이 포함됩니다.
fixBind 함수 선언: 바인딩이 날짜 유형일 때 수정하는 데 사용되는 보조 함수입니다.
EXEC 함수 선언: 기본 EXEC 헬퍼 함수입니다. 실행할 문, 바인드 배열(기본적으로 실행에 의해 수정될 수 있고 프로시저 실행 내내 데이터 영속성을 요구하는 변수 또는 매개 변수), ERROR_HANDLERS 를 사용해야 하는지 여부를 결정하는 noCatch 플래그, 문 실행에 예외가 있을 때 사용자 정의 코드를 실행하기 위한 catchFunction 함수를 받습니다. EXEC함수의 본문은 매우 간단합니다. 문을 실행하고 그 실행으로 생성된 모든 중요한 데이터를 오류 처리 블록에 저장합니다.
ERROR VARS: EXEC 캐치 블록은 SQL 서버_LINE](https://docs.microsoft.com/en-us/sql/t-sql/functions/error-line-transact-sql?view=sql-server-ver15),_MESSAGE](https://docs.microsoft.com/en-us/sql/t-sql/functions/error-message-transact-sql?view=sql-server-ver15),_NUMBER](https://docs.microsoft.com/en-us/sql/t-sql/functions/error-number-transact-sql?view=sql-server-ver15),_](https://docs.microsoft.com/en-us/sql/t-sql/functions/error-procedure-transact-sql?view=sql-server-ver15)및_](https://docs.microsoft.com/en-us/sql/t-sql/functions/error-state-transact-sql?view=sql-server-ver15) 내장 함수 동작을 에뮬레이션하기 위해 사용자 정의 함수에서 변수를 가져오기 위해 사용할 수 있는
MESSAGE_TEXT,SQLCODE,SQLSTATE,PROC_NAME,ERROR_LINE같은 오류 변수 목록을 설정합니다. 이러한 모든 변수가 1개의 값으로 설정된 후UPDATE_ERROR_VARS사용자 정의 함수는 SQL 범위에서 액세스하기 위해 일부 환경 변수를 오류 값으로 업데이트하는 작업을 담당합니다.
코드¶
다음 코드 블록은 프로시저 내부의 EXEC 헬퍼를 나타냅니다.
var _RS, ROW_COUNT, _ROWS, MESSAGE_TEXT, SQLCODE = 0, SQLSTATE = '00000', ERROR_HANDLERS, NUM_ROWS_AFFECTED, INTO;
var fixBind = function (arg) {
arg = arg == undefined ? null : arg instanceof Date ? arg.toISOString() : arg;
return arg;
};
var fetch = (count,rows,stmt) => (count && rows.next() && Array.apply(null,Array(stmt.getColumnCount())).map((_,i) => rows.getColumnValue(i + 1))) || [];
var EXEC = (stmt,binds = [],noCatch = false) => {
binds = binds ? binds.map(fixBind) : binds;
for(var stmt of stmt.split(";").filter((_) => _)) {
try {
_RS = snowflake.createStatement({
sqlText : stmt,
binds : binds
});
_ROWS = _RS.execute();
ROW_COUNT = _RS.getRowCount();
NUM_ROWS_AFFECTED = _RS.getNumRowsAffected();
return {
THEN : (action) => !SQLCODE && action(fetch(_ROWS))
};
} catch(error) {
let rStack = new RegExp('At .*, line (\\d+) position (\\d+)');
let stackLine = error.stackTraceTxt.match(rStack) || [0,-1];
MESSAGE_TEXT = error.message.toString();
SQLCODE = error.code.toString();
SQLSTATE = error.state.toString();
snowflake.execute({sqlText: `SELECT UPDATE_ERROR_VARS_UDF(?,?,?,?,?)`,binds: [stackLine[1], SQLCODE, SQLSTATE, MESSAGE_TEXT, PROC_NAME]});
throw error;
}
}
};
간단한 EXEC 예제
다음은 저장 프로시저 내부의 EXEC 호출의 간단한 예입니다
소스 코드
-- Additional Params: -t JavaScript
CREATE PROCEDURE dbo.EXEC_EXAMPLE_1
AS
EXECUTE('SELECT 1 AS Message');
GO
-- =============================================
-- Example to execute the stored procedure
-- =============================================
EXECUTE dbo.EXEC_EXAMPLE_1
GO
예상 코드
CREATE OR REPLACE PROCEDURE dbo.EXEC_EXAMPLE_1 ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// REGION SnowConvert AI Helpers Code
var _RS, ROW_COUNT, _ROWS, MESSAGE_TEXT, SQLCODE = 0, SQLSTATE = '00000', OBJECT_SCHEMA_NAME = 'dbo', ERROR_HANDLERS, NUM_ROWS_AFFECTED, PROC_NAME = arguments.callee.name, DOLLAR_DOLLAR = '$' + '$';
function* sqlsplit(sql) {
var part = '';
var ismark = () => sql[i] == '$' && sql[i + 1] == '$';
for(var i = 0;i < sql.length;i++) {
if (sql[i] == ';') {
yield part + sql[i];
part = '';
} else if (ismark()) {
part += sql[i++] + sql[i++];
while ( i < sql.length && !ismark() ) {
part += sql[i++];
}
part += sql[i] + sql[i++];
} else part += sql[i];
}
if (part.trim().length) yield part;
};
var formatDate = (arg) => (new Date(arg - (arg.getTimezoneOffset() * 60000))).toISOString().slice(0,-1);
var fixBind = function (arg) {
arg = arg == undefined ? null : arg instanceof Date ? formatDate(arg) : arg;
return arg;
};
var EXEC = (stmt,binds = [],severity = "16",noCatch = false) => {
binds = binds ? binds.map(fixBind) : binds;
for(var stmt of sqlsplit(stmt)) {
try {
_RS = snowflake.createStatement({
sqlText : stmt,
binds : binds
});
_ROWS = _RS.execute();
ROW_COUNT = _RS.getRowCount();
NUM_ROWS_AFFECTED = _RS.getNumRowsAffected();
return {
THEN : (action) => !SQLCODE && action(fetch(_ROWS))
};
} catch(error) {
let rStack = new RegExp('At .*, line (\\d+) position (\\d+)');
let stackLine = error.stackTraceTxt.match(rStack) || [0,-1];
MESSAGE_TEXT = error.message.toString();
SQLCODE = error.code.toString();
SQLSTATE = error.state.toString();
snowflake.execute({
sqlText : `SELECT UPDATE_ERROR_VARS_UDF(?,?,?,?,?,?)`,
binds : [stackLine[1],SQLCODE,SQLSTATE,MESSAGE_TEXT,PROC_NAME,severity]
});
throw error;
}
}
};
// END REGION
EXEC(`SELECT 1 AS Message;`);
$$;
매개 변수가 있는 저장 프로시저 내의 EXEC
이 예제에서 EXEC 명령은 저장 프로시저 내부에 있으며 매개 변수 값을 받습니다
소스 코드
-- Additional Params: -t JavaScript
CREATE PROCEDURE dbo.EXEC_EXAMPLE_2
@p1 varchar(50) = N''
AS
EXEC ('SELECT ' + @p1);
GO
-- =============================================
-- Example to execute the stored procedure
-- =============================================
EXECUTE dbo.EXEC_EXAMPLE_2 N'''Hello World!'''
GO
예상 코드
CREATE OR REPLACE PROCEDURE dbo.EXEC_EXAMPLE_2 (P1 STRING DEFAULT '')
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
EXEC(`SELECT
${P1};`);
$$;
EXEC 매개 변수를 사용하여 저장 프로시저 호출하기
이 예제에서 EXEC 은 다른 저장 프로시저를 호출하고 매개 변수를 추가합니다
소스 코드
-- Additional Params: -t JavaScript
CREATE PROCEDURE dbo.EXEC_EXAMPLE_3
@p1 varchar(50) = N''
AS
EXEC EXEC_EXAMPLE_2 @p1
GO
-- =============================================
-- Example to execute the stored procedure
-- =============================================
EXECUTE dbo.EXEC_EXAMPLE_3 N'''Hello World!'''
GO
예상 코드
CREATE OR REPLACE PROCEDURE dbo.EXEC_EXAMPLE_3 (P1 STRING DEFAULT '')
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
EXEC(`CALL EXEC_EXAMPLE_2(?)`,[P1]);
$$;
기본값이 있는 매개 변수.¶
SqlServer 에서는 프로시저가 호출될 때 지정되지 않은 경우 기본값이 있는 매개 변수가 있을 수 있습니다.
SQL 서버¶
CREATE PROCEDURE PROC_WITH_DEFAULT_PARAMS1
@PARAM1 INT = 0, @PARAM2 INT = 0, @PARAM3 INT = 0, @PARAM4 INT = 0
AS
BEGIN
.
.
.
END
Snowflake¶
CREATE OR REPLACE PROCEDURE PROC_WITH_DEFAULT_PARAMS1(param1 int default 0, param2 int default 0, param3 int default 0, param4 int default 0)
RETURNS TABLE()
LANGUAGE SQL
EXECUTE AS CALLER
AS
$$
.
.
.
$$;
CALL PROC_WITH_DEFAULT_PARAMS1(param2 => 10, param4 => 15);
CURSOR 헬퍼¶
CREATE OR REPLACE PROCEDURE PROC1()
RETURNS STRING
LANGUAGE JAVASCRIPT
EXECUTE AS CALLER
AS
$$
var CURSOR = function (stmt, binds) {
var statementObj, result_set, total_rows, isOpen = false, self = this, row_count;
this.CURRENT = new Object;
var fetch = (count,rows,stmt) => (count && rows.next() && Array.apply(null,Array(stmt.getColumnCount())).map((_,i) => rows.getColumnValue(i + 1))) || [];
var fixBind = function (arg) {
arg = arg == undefined ? null : arg instanceof Date ? formatDate(arg) : arg;
return arg;
};
this.OPEN = function(openParameters) {
if (result_set == undefined) {
try {
if (openParameters) binds = openParameters;
if (binds instanceof Function) binds = binds();
var finalBinds = binds && binds.map(fixBind);
var finalStmt = stmt instanceof Function ? stmt() : stmt;
statementObj = snowflake.createStatement({
sqlText : finalStmt,
binds : finalBinds
});
result_set = statementObj.execute();
total_rows = statementObj.getRowCount();
isOpen = true;
row_count = 0;
} catch(error) {
RAISE(error.code,"error",error.message);
}
else {
isOpen = true;
}
}
return this;
};
this.CURSOR_ROWS = function () {
return total_rows;
};
this.FETCH_STATUS = function() {
if(total_rows > row_count)
return 0;
else
return -1;
};
this.FETCH_NEXT = function() {
self.res = [];
if (isOpen) {
self.res = fetch(total_rows,result_set,statementObj);
if (self.res)
row_count++;
}
return self.res && self.res.length > 0;
};
this.INTO = function () {
return self.res;
};
this.CLOSE = function () {
isOpen = false;
};
this.DEALLOCATE = function() {
this.CURRENT = row_count = result_set_table = total_rows = result_set = statementObj = self = undefined;
};
};
var COL1, COL2;
var sql_stmt = ``;
let c = new CURSOR(`SELECT COL1, COL2 FROM TABLE1;`,() => []);
c.OPEN();
c.FETCH_NEXT();
[COL1, COL2] = c.INTO();
while ( c.FETCH_STATUS()) {
sql_stmt = `INSERT INTO TABLE2 (COL1, COL2) VALUES (` + COL1+ `, ` + COL2 + `)`;
snowflake.createStatement({
sqlText : sql_stmt
}).execute();
}
c.CLOSE();
c.DEALLOCATE();
return 'sucess';
$$;
EXEC 헬퍼에 삽입¶
Exec에 삽입 헬퍼는 Insert insertIntoTemporaryTable(sql) 이라는 함수를 생성합니다. 이 함수를 사용하면INSERT INTO TABLE_NAME EXEC(...) 을 TSQL 에서 Snowflake 로 변환하여 해당 데이터를 임시 테이블에 삽입한 다음 원래 Insert 에 다시 추가함으로써 원래 문의 동작을 모방할 수 있습니다.
이 문의 코드가 어떻게 수정되는지에 대한 자세한 내용은 실행에 삽입 섹션을 참조하십시오
참고
INSERT INTO EXEC 에 대해 생성된 코드는 내부에 여러 쿼리가 포함된 EXECUTE 문을 처리할 때 성능 문제가 발생할 수 있습니다.
function insertIntoTemporaryTable(sql) {
var table = "SnowConvertPivotTemporaryTable";
return EXEC('CREATE OR REPLACE TEMPORARY TABLE ${table} AS ${sql}');
}
insertIntoTemporaryTable(`${DBTABLES}`)
EXEC(`INSERT INTO MYDB.PUBLIC.T_Table SELECT * FROM MYDB.PUBLIC.SnowConvertPivotTemporaryTable`);
EXEC(`DROP TABLE SnowConvertPivotTemporaryTable`)
LIKE 헬퍼¶
프로시저에서 다음과 같은 식이 발견되는 경우, 예를 들어
CREATE PROCEDURE ProcedureLike @VariableValue VARCHAR(50) AS
BEGIN
IF @VariableValue like '%c%'
BEGIN
Select AValue from ATable;
END;
END;
프로시저의 내부가 Javascript로 변환되므로 같은 식은 오류를 throw합니다. 이 기능을 방지 및 유지하기 위해 프로시저를 시작할 때 같은 식이 발견되면 함수가 추가됩니다.
function LIKE(expr,pattern,esc,cs) {
function fixPattern(pattern,esc) {
const specials = '/.*+?|(){}[]\\'.split('');
var newPattern = "";
var fix = (c) => specials.includes(c) ? '\\' + c : c;
for(var i = 0;i < pattern.length;i++) {
var c = pattern[i];
if (c === esc) {
newPattern += pattern[i + 1]
i++
} else if (c === '%') {
newPattern += ".*?"
} else if (c === '_') {
newPattern += "."
} else if (c === '[' || ']') {
newPattern += c
} else newPattern += fix(c)
}
return newPattern;
}
return new RegExp(`^${fixPattern(pattern,esc)}$`,cs ? '' : 'i').exec(expr) != null;
}
이 함수를 사용하면 SQL의 like 식의 기능을 복제할 수 있습니다. 이 기능을 사용할 수 있는 다양한 케이스를 살펴보겠습니다
-- Additional Params: -t JavaScript
CREATE PROCEDURE ProcedureLike @VariableValue VARCHAR(50) AS
BEGIN
IF @VariableValue like '%c%'
BEGIN
Select AValue from ATable;
END;
IF @VariableValue not like '%c%'
BEGIN
Select BValue from BTable;
END;
IF @VariableValue like '%c!%%' escape '!'
BEGIN
Select CValue from CTable;
END;
END;
마지막 코드에는 normal like, not like 및 like with escape가 있습니다. 변환은 다음과 같습니다
CREATE OR REPLACE PROCEDURE ProcedureLike (VARIABLEVALUE STRING)
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
if (LIKE(VARIABLEVALUE,`%c%`)) {
{
EXEC(` Select
AValue
from
ATable`);
}
}
if (!LIKE(VARIABLEVALUE,`%c%`)) {
{
EXEC(` Select
BValue
from
BTable`);
}
}
if (LIKE(VARIABLEVALUE,`%c!%%`,`!`)) {
{
EXEC(` Select
CValue
from
CTable`);
}
}
$$;
like는 함수 호출로 변환됩니다
LIKE(VARIABLEVALUE,`%c%`)
!LIKE(VARIABLEVALUE,`%c%`)
LIKE(VARIABLEVALUE,`%c!%%`,`!`)
LIKE 함수가 수신하는 매개 변수는 다음과 같습니다.
평가 중인 식입니다.
비교 패턴
이스케이프 문자가 있는 경우 이스케이프 문자는 선택적 매개 변수입니다.
헬퍼 선택¶
스칼라 값을 변수로 설정해야 할 때 SELECT 라는 함수를 생성합니다
-- Additional Params: -t JavaScript
CREATE PROCEDURE MAX_EMPLOYEE_ID
AS
BEGIN
DECLARE @VARIABLE INT
SET @VARIABLE = (SELECT MAX(EMPLOYEE_ID) FROM EMPLOYEES);
RETURN @VARIABLE
END;
이 경우 SELECT 헬퍼를 사용하여 다음 코드를 생성합니다
CREATE OR REPLACE PROCEDURE MAX_EMPLOYEE_ID ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
let VARIABLE;
VARIABLE = EXEC(`SELECT
MAX(EMPLOYEE_ID) FROM
EMPLOYEES`);
return VARIABLE;
$$;
SELECT 헬퍼를 사용하여 쿼리에서 검색된 값을 로컬 값에 삽입할 수도 있습니다. 이 헬퍼는SQL 서버 SELECT @local_variable 와 동일한 동작을 지원하도록 특별히 설계되었습니다. args 매개 변수는 선택 내부의 모든 로컬 변수에 적용되는 각 작업을 나타냅니다. SELECT @Variable도 참조하십시오. 예:
-- Additional Params: -t JavaScript
CREATE PROCEDURE [PROCEDURE1] AS
DECLARE @VAR1 int;
DECLARE @VAR2 int;
select @VAR1 = col1 + col2, @VAR2 += col1 from table1;
GO
이 경우 변수 할당은 SQL 서버 동작을 에뮬레이션하기 위해 JavaScript lambdas로 변환됩니다.
CREATE OR REPLACE PROCEDURE PROCEDURE1 ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
let VAR1;
let VAR2;
SELECT(` col1 + col2,
col1
from
table1`,[],(value) => VAR1 = value,(value) => VAR2 += value);
$$;
RAISERROR 헬퍼¶
이 헬퍼는 소스 코드에RAISERROR호출의 사용처가 있을 때 생성됩니다. 예:
var RAISERROR = (message,severity,state) => {
snowflake.execute({
sqlText : `SELECT UPDATE_ERROR_VARS_UDF(?,?,?)`,
binds : [message,severity,state]
});
var msg = `Message: ${message}, Level: ${severity}, State: ${state}`;
throw msg;
};
RAISERROR 는 __UPDATE_ERROR_VARS_UDF_를 실행하여 오류 메시지, 심각도, 상태 값을 환경 변수로 저장하여 ERROR 기본 제공 함수 중 하나를 호출하여 사용해야 할 경우를 대비합니다. 마지막으로 SQL 서버와 동일한 형식으로 오류 메시지가 throw됩니다.
ID 함수 헬퍼¶
이 헬퍼는 프로시저 내부의 Select Into에서 ID 함수 를 사용할 때마다 생성됩니다.
var IdentityHelper = (seed,increment) => {
var sequenceString = "`CREATE OR REPLACE SEQUENCE SnowConvert_Temp_Seq START = ${seed} INCREMENT = ${increment}`";
return EXEC(sequenceString);
이 헬퍼의 매개 변수는 원래 함수와 동일하며, TSQL 에서 ID 함수 동작을 모방하는 시퀀스를 생성하기 위해 생성되며, 원본 코드의 변경 사항은 다음과 같습니다.
소스 코드에 있는 것과 동일한 매개 변수를 사용하여 IdentityHelper 함수에 대한 추가 메서드 호출입니다.
그리고 IDENTITY_UDF 함수를 호출하여 시퀀스의 다음 값을 가져옵니다.
IdentityHelper(1,1)
EXEC(`CREATE TABLE PUBLIC.department_table3 AS SELECT IDENTITY_UDF() /*** MSC-WARNING - MSCEWI1046 - 'identity' FUNCTION MAPPED TO 'IDENTITY_UDF', FUNCTIONAL EQUIVALENCE VERIFICATION PENDING ***/ as Primary_Rank
from PUBLIC.department_table`);
TSQL 에서와 마찬가지로 매개 변수를 지정하지 않으면 1,1이 기본값이 됩니다.
CALL 프로시저 헬퍼¶
이 헬퍼는 이전에는 사용자 정의 함수였지만 이제는 변환 프로세스의 결과로 프로시저가 된 함수에 대한 호출이 있을 때마다 생성됩니다.
var CALL = (sql,binds = [],...args) => {
EXEC("CALL " + sql,binds);
_ROWS.next();
return (_ROWS.getColumnValue(1))[0];
};
이 도우미의 목적은 프로시저를 호출하는 데 필요한 논리를 함수처럼 캡슐화하기 위한 것입니다.
SELECT 같은 쿼리 내에서는 프로시저를 호출할 수 없으므로 이 기능은 제한된다는 점에 유의하십시오.
FooSelfAssign(@PAR INT) 를 프로시저로 변환했다고 가정한 사용 예시입니다.
// Input code
DECLARE @VAR1 INT = FooSelfAssign(1);
DECLARE @VAR4 INT = FooSelfAssign(FooSelfAssign(FooSelfAssign(FooSelfAssign(4))));
// Output code
let VAR1 = CALL(`FooSelfAssign(1)`)
let VAR4 = CALL(`FooSelfAssign(?)`,[CALL(`FooSelfAssign(?)`,[CALL(`FooSelfAssign(?)`,[CALL(`FooSelfAssign(4)`)])])]);
VAR1 의 변환은 매우 간단하지만, VAR4 의 경우 가장 바깥쪽 CALL 에 나머지 CALLs 이 바인딩된 목록이 포함되어 있다는 점에 유의하십시오.
연속되는 각 CALL 은 다른 CALL 안에 포함되어 있는 경우 바인딩으로 변환됩니다.
2. Variables¶
DECLARE @변수¶
SQL 서버¶
DECLARE @product_list VARCHAR(MAX) = ' ';
DECLARE @Variable1 AS VARCHAR(100), @Variable2 AS VARCHAR(100);
Snowflake¶
let PRODUCT_LIST = ` `;
let VARIABLE1;
let VARIABLE2;
DECLARE 변수 테이블¶
이 경우 DECLARE 를 사용하여 변수 테이블을 선언합니다 예를 살펴보겠습니다.
-- Additional Params: -t JavaScript
CREATE PROCEDURE PROC1
AS
BEGIN
DECLARE @VariableNameTable TABLE
(
[Col1] INT NOT NULL,
[Col2] INT NOT NULL
);
INSERT INTO @VariableNameTable Values(111,222);
Select * from @VariableNameTable;
END
Exec PROC1;
Sql 서버에서 해당 코드를 실행하면 다음과 같은 결과를 얻을 수 있습니다
col1 |
col2 |
|---|---|
111 |
222 |
이제 Snowflake의 변화를 살펴 보겠습니다
CREATE OR REPLACE PROCEDURE PROC1 ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
{
EXEC(`CREATE OR REPLACE TEMPORARY TABLE T_VariableNameTable
(
Col1 INT NOT NULL,
Col2 INT NOT NULL
)`);
EXEC(`INSERT INTO T_VariableNameTable Values(111,222)`);
EXEC(`Select
*
from
T_VariableNameTable`);
}
EXEC(`CALL PROC1()`);
$$;
61 ~ 67 라인은 프로시저 내부의 해당 문의 결과입니다.
변수 선언 테이블이 임시 테이블로 바뀝니다. 이름에서 @ 문자가 T_로 대체되었다는 점에 유의하십시오.
Snowflake에서 해당 코드를 실행하면 아무런 결과도 얻지 못합니다. null만 표시됩니다. 그 이유는 마지막 Select가 이제 EXEC 헬퍼에 있기 때문입니다. 그렇다면 테이블이 있는지 어떻게 알 수 있을까요?
EXEC 의 프로시저 내부에 임시 테이블로 생성되었으므로 프로시저 외부에서 해당 테이블에 대한 선택을 수행할 수 있습니다.
Select * from PUBLIC.T_VariableNameTable;
해당 문을 실행하면 다음과 같은 결과가 표시됩니다
col1 |
col2 |
|---|---|
111 |
222 |
SET @변수¶
지금은 오른쪽에 있는 식에 따라 설정 변수가 변환됩니다.
식에 변환이 있는 경우 JavaScript 에 해당하는 값으로 변환됩니다.
예
-- Additional Params: -t JavaScript
CREATE PROCEDURE PROC1
AS
BEGIN
SET @product_list2 = '';
SET @product_list = '';
SET @var1 += '';
SET @var2 &= '';
SET @var3 ^= '';
SET @var4 |= '';
SET @var5 /= '';
SET @var6 %= '';
SET @var7 *= '';
SET @var8 -= '';
SET @ProviderStatement = 'SELECT * FROM TABLE1
WHERE COL1 = '+@PARAM1+ ' AND COL2 = ' + @LOCALVAR1;
SET @NotSupported = functionValue(a,b,c);
END
Snowflake¶
CREATE OR REPLACE PROCEDURE PROC1 ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
PRODUCT_LIST2 = ``;
PRODUCT_LIST = ``;
VAR1 += ``;
VAR2 &= ``;
VAR3 ^= ``;
VAR4 |= ``;
VAR5 /= ``;
VAR6 %= ``;
VAR7 *= ``;
VAR8 -= ``;
PROVIDERSTATEMENT = `SELECT
*
FROM
TABLE1
WHERE
COL1 = ${PARAM1}
AND COL2 = ${LOCALVAR1};`;
NOTSUPPORTED = SELECT(` functionValue(a,b,c) !!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'functionValue' NODE ***/!!!`);
$$;
예제에서 볼 수 있듯이 NOTSUPPORTED 변수의 값은 당분간 변환되지 않으므로 설명되어 있습니다. 이는 아직 변환이 완료되지 않았음을 의미합니다.
다른 종류의 세트는 다음과 같이 설명됩니다
SQL 서버¶
-- Additional Params: -t JavaScript
CREATE PROCEDURE PROC1
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
;
SET NOCOUNT ON
;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
;
SET NOCOUNT OFF
;
END
Snowflake¶
CREATE OR REPLACE PROCEDURE PROC1 ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
/*** SSC-EWI-0040 - THE 'SET' CLAUSE IS NOT SUPPORTED IN SNOWFLAKE ***/
/*SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED*/
;
/*** SSC-EWI-0040 - THE 'SET' CLAUSE IS NOT SUPPORTED IN SNOWFLAKE ***/
/*SET NOCOUNT ON*/
;
/*** SSC-EWI-0040 - THE 'SET' CLAUSE IS NOT SUPPORTED IN SNOWFLAKE ***/
/*SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED*/
;
/*** SSC-EWI-0040 - THE 'SET' CLAUSE IS NOT SUPPORTED IN SNOWFLAKE ***/
/*SET NOCOUNT OFF*/
;
$$;
SELECT @변수¶
지금은 SELECT @variable 를 단순 선택으로 변환하여 변수 할당을 제거하고 연산자 오른쪽에 식을 유지합니다. 선택에서 로컬 변수의 할당 작업은 SQL Server 에서 로컬 변수 할당 중에 수행되는 작업과 동일한 동작을 나타내는 화살표 함수로 대체됩니다.
SQL 서버¶
-- Additional Params: -t JavaScript
CREATE PROCEDURE PROC1 AS
DECLARE @VAR1 int;
DECLARE @VAR2 int;
SELECT @VAR1 = COL1 + COL2, @VAR2 = COL3 FROM TABLE1;
GO
Snowflake¶
CREATE OR REPLACE PROCEDURE PROC1 ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
let VAR1;
let VAR2;
SELECT(` COL1 + COL2,
COL3
FROM
TABLE1`,[],(value) => VAR1 = value,(value) => VAR2 = value);
$$;
3. Statements translation¶
SELECT¶
기본 양식¶
기본 SELECT 형식에는 바인딩이 없으므로 변환 중에 하나의 매개 변수를 사용하여 EXEC 헬퍼 함수가 호출됩니다. 예:
-- Source code:
SELECT * FROM DEMO_TABLE_1;
// Translated code:
EXEC(`SELECT * FROM DEMO_TABLE_1`);
IF¶
SQL 서버¶
IF Conditional_Expression
-- SQL Statement
ELSE IF Conditiona_Expression2
-- SQL Statement
ELSE
-- SQL Statement
Snowflake¶
if (Conditional_Expression) {
// SQL Statement
} else if (Conditional_Expression2) {
// SQL Statement
} else{
// SQL Statement
}
WHILE¶
SQL 서버¶
WHILE ( Conditional_Expression )
BEGIN
-- SQL STATEMENTS
END;
Snowflake¶
while ( Conditional_Expression )
{
// SQL STATEMENTS
}
EXEC / EXECUTE¶
SQL 서버¶
-- Execute simple statement
Exec('Select 1');
-- Execute statement using Dynamic Sql
Exec('Select ' + @par1 + ' from [db].[t1]');
-- Execute Procedure with parameter
EXEC db.sp2 'Create proc [db].[p3] AS', @par1, 1
Snowflake¶
-- Execute simple statement
EXEC(`Select 1`);
-- Execute statement using Dynamic Sql
EXEC(`Select ${PAR1} from MYDB.db.t1`);
-- Execute Procedure with parameter
EXEC(`CALL db.sp2(/*** SSC-EWI-0038 - THIS STATEMENT MAY BE A DYNAMIC SQL THAT COULD NOT BE RECOGNIZED AND CONVERTED ***/
'Select * from MYDB.db.t1', ?, 1, Default)`,[PAR1]);
THROW¶
THROW 에 대한 변환은 오류를 수신하는 캐치 블록이 원래 문에 지정된 정보에 액세스할 수 있도록 합니다.
SQL 서버¶
-- Case 1
THROW
-- Case 2
THROW 123, 'The error message', 1
-- Case 3
THROW @var1, @var2, @var3
Snowflake¶
// Case 1
throw {};
// Case 2
throw { code: 123, message: "The error message", status: 1 };
// Case 3
throw { code: VAR1, message: VAR2, status: VAR3 };
RAISERROR¶
SQL ServerRAISERROR 함수는 Snowflake에서 지원되지 않습니다. SnowConvert AI는 원래 동작을 에뮬레이션하는 헬퍼를 생성하기 위해 모든 사용을 식별합니다. 예:
SQL 서버¶
-- Additional Params: -t JavaScript
CREATE OR ALTER PROCEDURE RAISERRORTEST AS
BEGIN
DECLARE @MessageTXT VARCHAR = 'ERROR MESSAGE';
RAISERROR (N'E_INVALIDARG', 16, 1);
RAISERROR ('Diagram does not exist or you do not have permission.', 16, 1);
RAISERROR(@MessageTXT, 16, 1);
END
GO
Snowflake¶
CREATE OR REPLACE PROCEDURE RAISERRORTEST ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
let MESSAGETXT = `ERROR MESSAGE`;
RAISERROR("E_INVALIDARG","16","1");
RAISERROR("Diagram does not exist or you do not have permission.","16","1");
RAISERROR(MESSAGETXT,"16","1");
$$;
BREAK/CONTINUE¶
중단/계속 변환을 사용하면 코드의 플로우를 중단하거나 다른 블록으로 계속할 수 있습니다.
SQL 서버¶
-- Additional Params: -t JavaScript
CREATE PROCEDURE ProcSample
AS
BEGIN
IF @@ROWCOUNT > 0
Continue;
ELSE
BREAK;
END
Snowflake¶
CREATE OR REPLACE PROCEDURE ProcSample ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
if (ROW_COUNT > 0) {
continue;
} else {
break;
}
$$;
INSERT INTO EXEC¶
INSERT INTO[테이블] EXEC(...) 문은 Snowflake에서 지원되지 않기 때문에 코드를 약간 수정하여 몇 라인의 코드를 추가하여 동작을 복제할 수 있습니다.
추가된 첫 번째 라인은
EXEC내부의 인자에서 추출된 코드가 있는insertIntoTemporaryTable로 호출하여 결과 세트를 임시 테이블에 삽입합니다. 함수에 대한 자세한 내용은 EXEC 헬퍼에 삽입하기 섹션을 참조하십시오.코드에서 삽입의 Exec이 제거되고 임시 테이블에서 EXEC 의 결과를 검색하는 쿼리가 생성됩니다.
SELECT * FROM MYDB.PUBLIC.SnowConvertPivotTemporaryTable
추가된 마지막 줄은 추가된 임시 테이블에 대한 DROP TABLE 문입니다.
DROP TABLE SnowConvertPivotTemporaryTable
SQL 서버¶
INSERT INTO #Table1
EXEC ('SELECT
Table1.ID
FROM Population');
INSERT INTO #Table1
EXEC (@DBTables);
Snowflake¶
insertIntoTemporaryTable(`SELECT Table1.ID FROM MYDB.PUBLIC.Population)
EXEC(`INSERT INTO MYDB.PUBLIC.T_Table1 SELECT * FROM MYDB.PUBLIC.SnowConvertPivotTemporaryTable`);
EXEC(`DROP TABLE SnowConvertPivotTemporaryTable`)
insertIntoTemporaryTable(`${DBTABLES}`)
EXEC(`INSERT INTO MYDB.PUBLIC.T_Table1 SELECT * FROM MYDB.PUBLIC.SnowConvertPivotTemporaryTable`);
EXEC(`DROP TABLE SnowConvertPivotTemporaryTable`)
BEGIN TRANSACTION¶
BEGIN TRANSACTION 을 Snowflake의 BEGIN 명령으로 변환하여 EXEC 헬퍼 호출에 삽입합니다.
헬퍼는 결과 BEGIN 을 실제로 실행하는 역할을 담당합니다.
SQL 서버¶
-- Input code
BEGIN TRAN @transaction_name;
Snowflake¶
// Output code
EXEC(`BEGIN`, []);
COMMIT TRANSACTION¶
COMMIT TRANSACTION 을 Snowflake의 COMMIT 명령으로 변환하여 EXEC 헬퍼 호출에 삽입합니다.
헬퍼는 결과 COMMIT 를 실제로 실행하는 역할을 담당합니다.
SQL 서버¶
-- Input code
COMMIT TRAN @transaction_name;
Snowflake¶
// Output code
EXEC(`COMMIT`, []);
ROLLBACK TRANSACTION¶
ROLLBACK TRANSACTION 을 Snowflake의 ROLLBACK 명령으로 변환하여 EXEC 헬퍼 호출에 삽입합니다.
헬퍼는 결과물인 ROLLBACK 을 실제로 실행하는 역할을 담당합니다.
SQL 서버¶
-- Input code
ROLLBACK TRAN @transaction_name;
Snowflake¶
// Output code
EXEC(`ROLLBACK`, []);
WAITFOR DELAY¶
WAITFOR DELAY 절은 Snowflake의 SYSTEM$WAIT함 수로 변환됩니다. DELAY의 _time_to_pass_매개 변수는 SYSTEM$WAIT 함수의 매개 변수로 사용하기 위해 초 단위로 변환됩니다.
WAITFOR 절의 다른 베리언트는 Snowflake에서 지원되지 않으므로 해당 메시지가 표시됩니다.
SQL 서버¶
-- Input code
1) WAITFOR DELAY '02:00';
2) WAITFOR TIME '13:30';
3) WAITFOR (RECEIVE TOP (1)
@dh = conversation_handle,
@mt = message_type_name,
@body = message_body
FROM [eqe]), TIMEOUT 5000;
Snowflake¶
// Output code
1) EXEC(`SYSTEM$WAIT(120)`,[]);
2) /*** SSC-EWI-0040 - THE 'WAIT FOR' CLAUSE IS NOT SUPPORTED IN SNOWFLAKE ***/
/*WAITFOR TIME '13:30'*/
;
3) /*** SSC-EWI-0040 - THE 'WAIT FOR' CLAUSE IS NOT SUPPORTED IN SNOWFLAKE ***/
/*WAITFOR (RECEIVE TOP (1)
@dh = conversation_handle,
@mt = message_type_name,
@body = message_body
FROM [eqe]), TIMEOUT 5000*/
;
3. Cursors¶
CURSORS는 Snowflake에서 지원되지 않으므로 SnowConvert AI는 해당 기능을 대상 플랫폼에서 원래 동작을 에뮬레이션하는 JavaScript 헬퍼에 매핑합니다. 예:
SQL 서버¶
-- Additional Params: -t JavaScript
CREATE PROCEDURE [procCursorHelper] AS
DECLARE vendor_cursor CURSOR FOR
SELECT VendorID, Name
FROM Purchasing.Vendor
WHERE PreferredVendorStatus = 1
ORDER BY VendorID;
GO
Snowflake¶
CREATE OR REPLACE PROCEDURE procCursorHelper ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
var VENDOR_CURSOR = new CURSOR(`SELECT
VendorID,
Name
FROM
Purchasing.Vendor
WHERE
PreferredVendorStatus = 1
ORDER BY VendorID`,[],false);
$$;
DECLARE CURSOR¶
SQL 서버¶
DECLARE myCursor1 CURSOR FOR SELECT COL1 FROM TABLE1
Snowflake¶
let myCursor1 = new CURSOR(`SELECT COL1 FROM TABLE1`,() => []);
OPEN¶
SQL 서버¶
OPEN myCursor1
OPEN GLOBAL myCursor2
Snowflake¶
myCursor1.OPEN();
myCursor2.OPEN()
FETCH¶
SQL 서버¶
DECLARE @VALUE1 INT
FETCH NEXT FROM myCursor1 into @VALUE1
Snowflake¶
var VALUE1;
myCursor1.FETCH_NEXT();
VALUE1 = myCursor1.INTO();
CLOSE¶
SQL 서버¶
CLOSE myCursor1
CLOSE GLOBAL myCursor2
Snowflake¶
myCursor1.CLOSE()
myCursor2.CLOSE()
DEALLOCATE¶
SQL 서버¶
DEALLOCATE myCursor1
DEALLOCATE GLOBAL myCursor2
Snowflake¶
myCursor1.DEALLOCATE()
myCursor2.DEALLOCATE()
@@FETCH_STATUS¶
SQL 서버¶
@@FETCH_STATUS
Snowflake¶
myCursor1.FETCH_STATUS()
@@CURSOR_ROWS¶
SQL 서버¶
@@CURSOR_ROWS
Snowflake¶
myCursor1.FETCH_STATUS()
4. Expressions¶
이진 연산¶
SQL 서버¶
SET @var1 = 1 + 1;
SET @var1 = 1 - 1;
SET @var1 = 1 / 1;
SET @var1 = 1 * 1;
SET @var1 = 1 OR 1;
SET @var1 = 1 AND 1;
Snowflake¶
VAR1 = 1 + 1;
VAR1 = 1 - 1;
VAR1 = 1 / 1;
VAR1 = 1 * 1;
VAR1 = 1 || 1;
VAR1 = 1 && 1;
조건부¶
SQL 서버¶
@var1 > 0
@var1 = 0
@var1 < 0
@var1 <> 0
Snowflake¶
VAR1 > 0
VAR1 = 0
VAR1 < 0
VAR1 != 0
NULL 조건자¶
SQL 서버¶
@var1 is null
@var2 is not null
Snowflake¶
VAR1 == null
VAR2 != null
5. Labels and Goto¶
레이블 은 SQL Server에서 JavaScript 와 동일하게 동작하지 않습니다. 동작을 시뮬레이션하기 위해 함수 로 변환하고 있습니다. 그 사용법은 레이블의 모든 논리이 포함된 생성된 함수의 호출로 대체되고 있습니다. 예:
소스 코드¶
-- Additional Params: -t JavaScript
CREATE PROCEDURE [procWithLabels]
AS
SUCCESS_EXIT:
SET @ErrorStatus = 0
RETURN @ErrorStatus
ERROR_EXIT:
RETURN @ErrorStatus
Snowflake¶
CREATE OR REPLACE PROCEDURE procWithLabels ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
SUCCESS_EXIT();
ERROR_EXIT();
function SUCCESS_EXIT() {
ERRORSTATUS = 0;
return ERRORSTATUS;
}
function ERROR_EXIT() {
return ERRORSTATUS;
}
$$;
위의 예제에서 볼 수 있듯이 소스 코드의 레이블이었던 함수 선언을 코드 끝에 배치하여 코드를 더 깔끔하게 생성할 수 있습니다.
GOTO 는 JavaScript 에는 존재하지 않는 또 다른 명령입니다. 동작을 시뮬레이션하기 위해 참조되는 함수(레이블)에 대한 호출로 변환하고 그 앞에 반환 문을 붙였습니다. 예:
SQL 서버¶
-- Additional Params: -t JavaScript
CREATE PROCEDURE [procWithLabels]
AS
DECLARE @ErrorStatus int = 0;
IF @ErrorStatus <> 0 GOTO ERROR_EXIT
SUCCESS_EXIT:
SET @ErrorStatus = 0
RETURN @ErrorStatus
ERROR_EXIT:
RETURN @ErrorStatus
Snowflake¶
CREATE OR REPLACE PROCEDURE procWithLabels ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.
let ERRORSTATUS = 0;
if (ERRORSTATUS != 0) {
return ERROR_EXIT();
}
SUCCESS_EXIT();
ERROR_EXIT();
function SUCCESS_EXIT() {
ERRORSTATUS = 0;
return ERRORSTATUS;
}
function ERROR_EXIT() {
return ERRORSTATUS;
}
$$;
위의 예제에서 볼 수 있듯이 SQL 서버가 GOTO 에서와 같이 코드 플로우를 중단하기 위해 함수 호출에 return 이 추가되었습니다.