SnowConvert AI - Serveur SQL - CREATE FUNCTION¶
Référence de traduction pour les fonctions définies par l’utilisateur Transact-SQL
Applies to
SQL Server
Azure Synapse Analytics
Description¶
SQL Server ne prend en charge que deux types de fonctions définies par l’utilisateur :
À partir de ces types d’UDFs, il est possible de les sous-catégoriser en simples et complexes, selon la logique interne.
UDFs simples, fait correspondre la syntaxe du serveur SQL à la syntaxe de Snowflake. Ce type n’ajoute aucune logique et va directement au résultat. Ils correspondent généralement aux SQL UDFs de Snowflake. SnowConvert prend en charge la traduction des fonctions d’utilisateur scalaires du serveur SQL directement vers les UDFs Exécution de scripts Snowflake lorsqu’ils répondent à des critères spécifiques.\ \ UDFs complexes, fait un usage courant d’une instruction particulière (INSERT, DELETE, UPDATE, SET, DECLARE, etc) or control-of-flow blocks (IF…ELSE, WHILE, etc) et représente généralement une erreur de correspondance ou une violation de la définition des SQL UDFs de Snowflake.
Limitations¶
Les UDFs Transact présentent certaines limites qui ne sont pas présentes dans d’autres moteurs de base de données (comme Oracle et Teradata). Ces limites facilitent les traductions en réduisant le champ d’application de l’échec. Cela signifie qu’il existe des scénarios spécifiques que nous pouvons espérer éviter.
Voici quelques-unes des limites de SQL Server applicables aux UDFs
Les UDFs ne peuvent pas être utilisées pour effectuer des actions qui modifient l’état de la base de données
Les fonctions définies par l’utilisateur ne peuvent pas contenir de clause OUTPUT INTO ayant une table pour cible
Les fonctions définies par l’utilisateur ne peuvent pas renvoyer plusieurs jeux de résultats. Utilisez une procédure stockée si vous devez renvoyer plusieurs jeux de résultats.
Pour obtenir la liste complète, consultez le lien Créer des fonctions définies par l’utilisateur (moteur de base de données)
INLINE TABLE-VALUED¶
Référence de traduction pour convertir une UDF Transact-SQL (Fonctions définies par l’utilisateur) avec type de retour TABLE vers Snowflake.
Applies to
SQL Server
Azure Synapse Analytics
Description¶
Note
Certaines parties du code de sortie sont omises pour des raisons de clarté.
Les fonctions en ligne à valeur de table sont des expressions de table qui peuvent accepter des paramètres, exécuter une instruction SELECT et renvoyer une TABLE (Référence linguistique SQL Server Création d’une fonction à valeur de table intégrée.)
Syntaxe Transact¶
-- Transact-SQL Inline Table-Valued Function Syntax
CREATE [ OR ALTER ] FUNCTION [ schema_name. ] function_name
( [ { @parameter_name [ AS ] [ type_schema_name. ] parameter_data_type
[ = default ] [ READONLY ] }
[ ,...n ]
]
)
RETURNS TABLE
[ WITH <function_option> [ ,...n ] ]
[ AS ]
RETURN [ ( ] select_stmt [ ) ]
[ ; ]
Syntaxe Snowflake SQL¶
CREATE OR REPLACE FUNCTION <name> ( [ <arguments> ] )
RETURNS TABLE ( <output_col_name> <output_col_type> [, <output_col_name> <output_col_type> ... ] )
AS '<sql_expression>'sql
Modèles d’échantillons de sources¶
La section suivante décrit tous les modèles de code source pouvant apparaître dans ce type de syntaxe CREATE FUNCTION.
Pour les fonctions en ligne à valeur de table, il ne peut y avoir qu’une seule instruction par corps qui puisse être :
Instruction
SELECTExpression de table commune
WITH
Sélectionner et retourner des valeurs directement à partir d’une table¶
Il s’agit du scénario le plus simple, qui consiste à effectuer une simple sélection dans une table et à renvoyer les valeurs correspondantes
Transact-SQL¶
Valeur de table en ligne¶
CREATE FUNCTION GetDepartmentInfo()
RETURNS TABLE
AS
RETURN
(
SELECT DepartmentID, Name, GroupName
FROM HumanResources.Department
);
GO
SELECT * from GetDepartmentInfo()
Résultat¶
DepartmentID |
Nom |
GroupName |
|---|---|---|
1 |
Engineering |
Recherche et développement |
2 |
Conception d’outils |
Recherche et développement |
3 |
Sales |
Ventes et marketing |
4 |
Marketing |
Ventes et marketing |
5 |
Achats |
Gestion des stocks |
6 |
Recherche et développement |
Recherche et développement |
7 |
Production |
Fabrication |
8 |
Contrôle de la production |
Fabrication |
9 |
Ressources humaines |
Exécution générale et administration |
10 |
Finances |
Exécution générale et administration |
11 |
Services d’information |
Exécution générale et administration |
12 |
Contrôle des documents |
Assurance qualité |
13 |
Assurance qualité |
Assurance qualité |
14 |
Installations et entretien |
Exécution générale et administration |
15 |
Expédition et réception |
Gestion des stocks |
16 |
Exécution |
Exécution générale et administration |
Snowflake SQL¶
Valeur de table en ligne¶
CREATE OR REPLACE FUNCTION GetDepartmentInfo ()
RETURNS TABLE(
DepartmentID STRING /*** SSC-FDM-TS0012 - INFORMATION FOR THE COLUMN DepartmentID WAS NOT FOUND. STRING DATATYPE USED TO MATCH CAST AS STRING OPERATION ***/,
Name STRING /*** SSC-FDM-TS0012 - INFORMATION FOR THE COLUMN Name WAS NOT FOUND. STRING DATATYPE USED TO MATCH CAST AS STRING OPERATION ***/,
GroupName STRING /*** SSC-FDM-TS0012 - INFORMATION FOR THE COLUMN GroupName WAS NOT FOUND. STRING DATATYPE USED TO MATCH CAST AS STRING OPERATION ***/
)
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
AS
$$
SELECT
CAST(DepartmentID AS STRING),
CAST(Name AS STRING),
CAST(GroupName AS STRING)
FROM
HumanResources.Department
$$;
SELECT
*
from
TABLE(GetDepartmentInfo());
Résultat¶
DepartmentID |
Nom |
GroupName |
|---|---|---|
1 |
Engineering |
Recherche et développement |
2 |
Conception d’outils |
Recherche et développement |
3 |
Sales |
Ventes et marketing |
4 |
Marketing |
Ventes et marketing |
5 |
Achats |
Gestion des stocks |
6 |
Recherche et développement |
Recherche et développement |
7 |
Production |
Fabrication |
8 |
Contrôle de la production |
Fabrication |
9 |
Ressources humaines |
Exécution générale et administration |
10 |
Finances |
Exécution générale et administration |
11 |
Services d’information |
Exécution générale et administration |
12 |
Contrôle des documents |
Assurance qualité |
13 |
Assurance qualité |
Assurance qualité |
14 |
Installations et entretien |
Exécution générale et administration |
15 |
Expédition et réception |
Gestion des stocks |
16 |
Exécution |
Exécution générale et administration |
Sélectionner et retourner des valeurs à partir de plusieurs tables en renommant les colonnes et en utilisant des fonctions intégrées¶
Voici un exemple de requête utilisant des fonctions intégrées dans une instruction Select pour obtenir des données de différentes tables, renommer les colonnes et renvoyer une table.
Transact-SQL¶
Valeur de table en ligne¶
CREATE FUNCTION GetPersonBasicInfo()
RETURNS TABLE
AS
RETURN
(
SELECT TOP (20)
P.PersonType,
P.FirstName,
E.JobTitle,
E.Gender,
YEAR(E.HireDate) as HIREYEAR
FROM
Person.Person P
INNER JOIN
HumanResources.Employee E
ON
P.BusinessEntityID = E.BusinessEntityID
);
GO
SELECT * FROM GetPersonBasicInfo();
Résultat¶
PersonType |
FirstName |
JobTitle |
Sexe |
HIREYEAR |
|---|---|---|---|---|
EM |
Ken |
Directeur général |
M |
2009 |
EM |
Terri |
Vice-président de l’ingénierie |
F |
2008 |
EM |
Roberto |
Directeur de l’ingénierie |
M |
2007 |
EM |
Rob |
Concepteur d’outils en chef |
M |
2007 |
EM |
Gail |
Ingénieur conception |
F |
2008 |
EM |
Jossef |
Ingénieur conception |
M |
2008 |
EM |
Dylan |
Directeur de la recherche et du développement |
M |
2009 |
EM |
Diane |
Ingénieur Recherche et Développement |
F |
2008 |
EM |
Gigi |
Ingénieur Recherche et Développement |
F |
2009 |
EM |
Michael |
Directeur de la recherche et du développement |
M |
2009 |
EM |
Ovidiu |
Concepteur d’outils en chef |
M |
2010 |
EM |
Thierry |
Créateur d’outils |
M |
2007 |
EM |
Janice |
Créateur d’outils |
F |
2010 |
EM |
Michael |
Ingénieur conception |
M |
2010 |
EM |
Sharon |
Ingénieur conception |
F |
2011 |
EM |
David |
Directeur du marketing |
M |
2007 |
EM |
Kevin |
Assistant marketing |
M |
2007 |
EM |
John |
Spécialiste marketing |
M |
2011 |
EM |
Mary |
Assistant marketing |
F |
2011 |
EM |
Wanida |
Assistant marketing |
F |
2011 |
Snowflake SQL¶
Valeur de table en ligne¶
CREATE OR REPLACE FUNCTION GetPersonBasicInfo ()
RETURNS TABLE(
PersonType STRING /*** SSC-FDM-TS0012 - INFORMATION FOR THE COLUMN PersonType WAS NOT FOUND. STRING DATATYPE USED TO MATCH CAST AS STRING OPERATION ***/,
FirstName STRING /*** SSC-FDM-TS0012 - INFORMATION FOR THE COLUMN FirstName WAS NOT FOUND. STRING DATATYPE USED TO MATCH CAST AS STRING OPERATION ***/,
JobTitle STRING /*** SSC-FDM-TS0012 - INFORMATION FOR THE COLUMN JobTitle WAS NOT FOUND. STRING DATATYPE USED TO MATCH CAST AS STRING OPERATION ***/,
Gender STRING /*** SSC-FDM-TS0012 - INFORMATION FOR THE COLUMN Gender WAS NOT FOUND. STRING DATATYPE USED TO MATCH CAST AS STRING OPERATION ***/,
HIREYEAR INTEGER
)
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
AS
$$
SELECT
TOP 20
CAST(P.PersonType AS STRING),
CAST(P.FirstName AS STRING),
CAST(E.JobTitle AS STRING),
CAST(E.Gender AS STRING),
YEAR(E.HireDate :: TIMESTAMP) as HIREYEAR
FROM
Person.Person P
INNER JOIN
HumanResources.Employee E
ON P.BusinessEntityID = E.BusinessEntityID
$$;
SELECT
*
FROM
TABLE(GetPersonBasicInfo());
Résultat¶
PersonType |
FirstName |
JobTitle |
Sexe |
HIREYEAR |
|---|---|---|---|---|
EM |
Ken |
Directeur général |
M |
2009 |
EM |
Terri |
Vice-président de l’ingénierie |
F |
2008 |
EM |
Roberto |
Directeur de l’ingénierie |
M |
2007 |
EM |
Rob |
Concepteur d’outils en chef |
M |
2007 |
EM |
Gail |
Ingénieur conception |
F |
2008 |
EM |
Jossef |
Ingénieur conception |
M |
2008 |
EM |
Dylan |
Directeur de la recherche et du développement |
M |
2009 |
EM |
Diane |
Ingénieur Recherche et Développement |
F |
2008 |
EM |
Gigi |
Ingénieur Recherche et Développement |
F |
2009 |
EM |
Michael |
Directeur de la recherche et du développement |
M |
2009 |
EM |
Ovidiu |
Concepteur d’outils en chef |
M |
2010 |
EM |
Thierry |
Créateur d’outils |
M |
2007 |
EM |
Janice |
Créateur d’outils |
F |
2010 |
EM |
Michael |
Ingénieur conception |
M |
2010 |
EM |
Sharon |
Ingénieur conception |
F |
2011 |
EM |
David |
Directeur du marketing |
M |
2007 |
EM |
Kevin |
Assistant marketing |
M |
2007 |
EM |
John |
Spécialiste marketing |
M |
2011 |
EM |
Mary |
Assistant marketing |
F |
2011 |
EM |
Wanida |
Assistant marketing |
F |
2011 |
Sélectionnez des colonnes à l’aide de l’instruction WITH¶
Le corps d’une fonction à valeur de table intégrée peut également être spécifié à l’aide d’une instruction WITH, comme indiqué ci-dessous.
Transact-SQL¶
Valeur de table en ligne¶
CREATE FUNCTION GetMaritalStatusByGender
(
@P_Gender nchar(1)
)
RETURNS TABLE
AS
RETURN
(
WITH CTE AS
(
SELECT BusinessEntityID, MaritalStatus, Gender
FROM HumanResources.Employee
where Gender = @P_Gender
)
SELECT
MaritalStatus, Gender, CONCAT(P.FirstName,' ', P.LastName) as Name
FROM
CTE INNER JOIN Person.Person P
ON
CTE.BusinessEntityID = P.BusinessEntityID
);
GO
select * from GetMaritalStatusByGender('F');
Résultat¶
MaritalStatus |
Sexe |
Nom |
|---|---|---|
S |
F |
Terri Duffy |
M |
F |
Gail Erickson |
S |
F |
Diane Margheim |
M |
F |
Gigi Matthew |
M |
F |
Janice Galvin |
M |
F |
Sharon Salavaria |
S |
F |
Mary Dempsey |
M |
F |
Wanida Benshoof |
M |
F |
Mary Gibson |
M |
F |
Jill Williams |
S |
F |
Jo Brown |
M |
F |
Britta Simon |
M |
F |
Margie Shoop |
M |
F |
Rebecca Laszlo |
M |
F |
Suchitra Mohan |
M |
F |
Kim Abercrombie |
S |
F |
JoLynn Dobney |
M |
F |
Nancy Anderson |
M |
F |
Ruth Ellerbrock |
M |
F |
Doris Hartwig |
M |
F |
Diane Glimp |
M |
F |
Bonnie Kearney |
M |
F |
Denise Smith |
S |
F |
Diane Tibbott |
M |
F |
Carole Poland |
M |
F |
Carol Philips |
M |
F |
Merav Netz |
S |
F |
Betsy Stadick |
S |
F |
Danielle Tiedt |
S |
F |
Kimberly Zimmerman |
M |
F |
Elizabeth Keyser |
M |
F |
Mary Baker |
M |
F |
Alice Ciccu |
M |
F |
Linda Moschell |
S |
F |
Angela Barbariol |
S |
F |
Kitti Lertpiriyasuwat |
S |
F |
Susan Eaton |
S |
F |
Kim Ralls |
M |
F |
Nicole Holliday |
S |
F |
Anibal Sousa |
M |
F |
Samantha Smith |
S |
F |
Olinda Turner |
S |
F |
Cynthia Randall |
M |
F |
Sandra Reátegui Alayo |
S |
F |
Linda Randall |
S |
F |
Shelley Dyck |
S |
F |
Laura Steele |
S |
F |
Susan Metters |
S |
F |
Katie McAskill-White |
M |
F |
Barbara Decker |
M |
F |
Yvonne McKay |
S |
F |
Janeth Esteves |
M |
F |
Brenda Diaz |
M |
F |
Loren Line |
M |
F |
Paula Nartker |
S |
F |
Lori Kane |
M |
F |
Kathie Flood |
S |
F |
Belinda Newman |
M |
F |
Karen Berge |
M |
F |
Lori Penor |
M |
F |
Jo Berry |
M |
F |
Laura Norman |
M |
F |
Paula Barreto de Mattos |
M |
F |
Mindy Martin |
M |
F |
Deborah Poe |
S |
F |
Candy Spoon |
M |
F |
Barbara Moreland |
M |
F |
Janet Sheperdigian |
S |
F |
Wendy Kahn |
S |
F |
Sheela Word |
M |
F |
Linda Meisner |
S |
F |
Erin Hagens |
M |
F |
Annette Hill |
S |
F |
Jean Trenary |
S |
F |
Stephanie Conroy |
S |
F |
Karen Berg |
M |
F |
Janaina Bueno |
M |
F |
Linda Mitchell |
S |
F |
Jillian Carson |
S |
F |
Pamela Ansman-Wolfe |
S |
F |
Lynn Tsoflias |
M |
F |
Amy Alberts |
S |
F |
Rachel Valdez |
M |
F |
Jae Pak |
Snowflake SQL¶
Valeur de table en ligne¶
--** SSC-FDM-0007 - MISSING DEPENDENT OBJECTS "HumanResources.Employee", "Person.Person" **
CREATE OR REPLACE FUNCTION GetMaritalStatusByGender
(P_GENDER STRING
)
RETURNS TABLE(
MaritalStatus STRING /*** SSC-FDM-TS0012 - INFORMATION FOR THE COLUMN MaritalStatus WAS NOT FOUND. STRING DATATYPE USED TO MATCH CAST AS STRING OPERATION ***/,
Gender STRING /*** SSC-FDM-TS0012 - INFORMATION FOR THE COLUMN Gender WAS NOT FOUND. STRING DATATYPE USED TO MATCH CAST AS STRING OPERATION ***/,
Name VARCHAR
)
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "transact", "convertedOn": "07/11/2025", "domain": "no-domain-provided" }}'
AS
$$
--** SSC-PRF-TS0001 - PERFORMANCE WARNING - RECURSION FOR CTE NOT CHECKED. MIGHT REQUIRE RECURSIVE KEYWORD **
WITH CTE AS
(
SELECT
BusinessEntityID,
MaritalStatus,
Gender
FROM
HumanResources.Employee
where
Gender = :P_GENDER
)
SELECT
CAST(MaritalStatus AS STRING),
CAST(Gender AS STRING),
CONCAT(P.FirstName,' ', P.LastName) as Name
FROM
CTE
INNER JOIN
Person.Person P
ON CTE.BusinessEntityID = P.BusinessEntityID
$$;
select
*
from
TABLE(GetMaritalStatusByGender('F'));
Résultat¶
MaritalStatus |
Sexe |
Nom |
|---|---|---|
S |
F |
Terri Duffy |
M |
F |
Gail Erickson |
S |
F |
Diane Margheim |
M |
F |
Gigi Matthew |
M |
F |
Janice Galvin |
M |
F |
Sharon Salavaria |
S |
F |
Mary Dempsey |
M |
F |
Wanida Benshoof |
M |
F |
Mary Gibson |
M |
F |
Jill Williams |
S |
F |
Jo Brown |
M |
F |
Britta Simon |
M |
F |
Margie Shoop |
M |
F |
Rebecca Laszlo |
M |
F |
Suchitra Mohan |
M |
F |
Kim Abercrombie |
S |
F |
JoLynn Dobney |
M |
F |
Nancy Anderson |
M |
F |
Ruth Ellerbrock |
M |
F |
Doris Hartwig |
M |
F |
Diane Glimp |
M |
F |
Bonnie Kearney |
M |
F |
Denise Smith |
S |
F |
Diane Tibbott |
M |
F |
Carole Poland |
M |
F |
Carol Philips |
M |
F |
Merav Netz |
S |
F |
Betsy Stadick |
S |
F |
Danielle Tiedt |
S |
F |
Kimberly Zimmerman |
M |
F |
Elizabeth Keyser |
M |
F |
Mary Baker |
M |
F |
Alice Ciccu |
M |
F |
Linda Moschell |
S |
F |
Angela Barbariol |
S |
F |
Kitti Lertpiriyasuwat |
S |
F |
Susan Eaton |
S |
F |
Kim Ralls |
M |
F |
Nicole Holliday |
S |
F |
Anibal Sousa |
M |
F |
Samantha Smith |
S |
F |
Olinda Turner |
S |
F |
Cynthia Randall |
M |
F |
Sandra Reátegui Alayo |
S |
F |
Linda Randall |
S |
F |
Shelley Dyck |
S |
F |
Laura Steele |
S |
F |
Susan Metters |
S |
F |
Katie McAskill-White |
M |
F |
Barbara Decker |
M |
F |
Yvonne McKay |
S |
F |
Janeth Esteves |
M |
F |
Brenda Diaz |
M |
F |
Loren Line |
M |
F |
Paula Nartker |
S |
F |
Lori Kane |
M |
F |
Kathie Flood |
S |
F |
Belinda Newman |
M |
F |
Karen Berge |
M |
F |
Lori Penor |
M |
F |
Jo Berry |
M |
F |
Laura Norman |
M |
F |
Paula Barreto de Mattos |
M |
F |
Mindy Martin |
M |
F |
Deborah Poe |
S |
F |
Candy Spoon |
M |
F |
Barbara Moreland |
M |
F |
Janet Sheperdigian |
S |
F |
Wendy Kahn |
S |
F |
Sheela Word |
M |
F |
Linda Meisner |
S |
F |
Erin Hagens |
M |
F |
Annette Hill |
S |
F |
Jean Trenary |
S |
F |
Stephanie Conroy |
S |
F |
Karen Berg |
M |
F |
Janaina Bueno |
M |
F |
Linda Mitchell |
S |
F |
Jillian Carson |
S |
F |
Pamela Ansman-Wolfe |
S |
F |
Lynn Tsoflias |
M |
F |
Amy Alberts |
S |
F |
Rachel Valdez |
M |
F |
Jae Pak |
Problèmes connus¶
Aucun problème n’a été trouvé
MULTI-STATEMENT TABLE-VALUED¶
Référence de traduction pour convertir une UDF Transact-SQL (Fonctions définies par l’utilisateur) avec type de retour TABLE vers Snowflake.
Applies to
SQL Server
Azure Synapse Analytics
Note
Certaines parties du code de sortie sont omises pour des raisons de clarté.
Note
Tous les exemples de code de cette page n’ont pas encore été implémentés dans SnowConvert AI. Ils doivent être interprétés comme une référence pour la manière dont chaque scénario doit être traduit dans Snowflake. Ces traductions peuvent changer à l’avenir. Certaines parties du code de sortie sont omises pour des raisons de clarté.
Description¶
La valeur de table multi-instructions est similaire à la valeur de table des instructions en ligne (INLINE TABLE-VALUED). Cependant, les valeurs de table multi-instructions peuvent avoir plusieurs instructions dans son corps de fonction, les colonnes de la table sont spécifiées dans le type de retour et il a un bloc BEGIN/END (Référence du langage du serveur SQL créant une fonction à valeur de table multi-instructions
Syntaxe Transact-SQL¶
CREATE [ OR ALTER ] FUNCTION [ schema_name. ] function_name
( [ { @parameter_name [ AS ] [ type_schema_name. ] parameter_data_type
[ = default ] [READONLY] }
[ ,...n ]
]
)
RETURNS @return_variable TABLE <table_type_definition>
[ WITH <function_option> [ ,...n ] ]
[ AS ]
BEGIN
function_body
RETURN
END
[ ; ]
Snowflake SQL¶
CREATE OR REPLACE FUNCTION <name> ( [ <arguments> ] )
RETURNS TABLE ( <output_col_name> <output_col_type> [, <output_col_name> <output_col_type> ... ] )
AS '<sql_expression>'
Modèles d’échantillons de sources¶
La section suivante décrit tous les modèles de code source possibles qui peuvent apparaître dans ce type de syntaxe ofCREATE FUNCTION.
Le corps de la fonction à valeur de table multi-instructions doit être une instruction SELECT. C’est pourquoi les autres instructions doivent être appelées séparément.
Insérer des valeurs dans une table¶
Insère une ou plusieurs lignes dans la table et renvoie la table avec les nouvelles valeurs
Transact-SQL¶
MULTI-STATEMENT TABLE-VALUED¶
CREATE OR ALTER FUNCTION calc_behavioral_segment()
RETURNS @behavioral_segments TABLE (behavioral_segment VARCHAR(50))
AS
BEGIN
DECLARE @col varchar(15)
SET @col = 'Unknown'
INSERT INTO @behavioral_segments
SELECT @col
RETURN
END
SELECT * FROM calc_behavioral_segment();
Résultat¶
BEHAVIORAL_SEGMENT |
|---|
Inconnu |
Snowflake SQL¶
MULTI-STATEMENT TABLE-VALUED¶
!!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'TABLE VALUED FUNCTIONS' NODE ***/!!!
CREATE OR ALTER FUNCTION calc_behavioral_segment ()
RETURNS BEHAVIORAL_SEGMENTS TABLE (
behavioral_segment VARCHAR(50))
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
BEGIN
DECLARE @col varchar(15)
SET @col = 'Unknown'
INSERT INTO @behavioral_segments
SELECT @col
RETURN
END
SELECT * FROM calc_behavioral_segment();;
Résultats¶
BEHAVIORAL_SEGMENT |
|---|
Inconnu |
Insérer une valeur en fonction de l’instruction if/else¶
Insère une ligne dans la table conformément à la condition et renvoie la table avec la nouvelle valeur
Transact-SQL¶
MULTI-STATEMENT TABLE-VALUED¶
CREATE OR ALTER FUNCTION odd_or_even_number(@number INT)
RETURNS @numbers TABLE (number_type VARCHAR(15))
AS
BEGIN
IF ((@number % 2) = 0)
BEGIN
INSERT @numbers SELECT 'Even'
END
ELSE
BEGIN
INSERT @numbers SELECT 'Odd'
END
RETURN
END
SELECT * FROM odd_or_even_number(9);
Résultat¶
NUMBER_TYPE |
|---|
Impair |
Snowflake SQL¶
MULTI-STATEMENT TABLE-VALUED¶
!!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'TABLE VALUED FUNCTIONS' NODE ***/!!!
CREATE OR ALTER FUNCTION odd_or_even_number (NUMBER INT)
RETURNS NUMBERS TABLE (
number_type VARCHAR(15))
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
BEGIN
IF ((@number % 2) = 0)
BEGIN
INSERT @numbers SELECT 'Even'
END
ELSE
BEGIN
INSERT @numbers SELECT 'Odd'
END
RETURN
END
SELECT * FROM odd_or_even_number(9);;
Résultat¶
NUMBER_TYPE |
|---|
Impair |
Insère plusieurs éléments en fonction de l’instruction if/else¶
L’exemple ci-dessous insère plusieurs valeurs dans la table et plusieurs variables sont modifiées en fonction de la condition. Renvoie la table contenant les nouvelles valeurs
Transact-SQL¶
MULTI-STATEMENT TABLE-VALUED¶
CREATE OR ALTER FUNCTION new_employee_hired(@id VARCHAR (50), @position VARCHAR(50), @experience VARCHAR(15))
RETURNS @new_employee TABLE (id_employee VARCHAR (50), working_from_home BIT, team VARCHAR(15), computer VARCHAR(15))
AS
BEGIN
DECLARE @wfh BIT
DECLARE @team VARCHAR(15)
DECLARE @computer VARCHAR(15)
IF @position = 'DEVELOPER'
BEGIN
SET @team = 'TEAM_1'
SET @computer = 'LAPTOP'
END
IF @position = 'IT'
BEGIN
SET @team = 'TEAM_2'
SET @computer = 'DESKTOP'
END
IF @experience = 'JUNIOR'
BEGIN
SET @wfh = '0'
END
IF @experience = 'SENIOR'
BEGIN
SET @wfh = '1'
END
INSERT INTO @new_employee VALUES (@id, @wfh, @team, @computer)
RETURN
END
SELECT * FROM new_employee_hired('123456789', 'DEVELOPER', 'SENIOR');
Résultat¶
ID_EMPLOYEE |
WORKING_FROM_HOME |
TEAM |
COMPUTER |
|---|---|---|---|
123456789 |
1 |
TEAM_1 |
LAPTOP |
Snowflake¶
MULTI-STATEMENT TABLE-VALUED¶
!!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'TABLE VALUED FUNCTIONS' NODE ***/!!!
CREATE OR ALTER FUNCTION new_employee_hired (ID STRING, POSITION STRING, EXPERIENCE STRING)
RETURNS NEW_EMPLOYEE TABLE (
id_employee VARCHAR(50),
working_from_home BOOLEAN,
team VARCHAR(15),
computer VARCHAR(15))
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
BEGIN
DECLARE @wfh BIT
DECLARE @team VARCHAR(15)
DECLARE @computer VARCHAR(15)
IF @position = 'DEVELOPER'
BEGIN
SET @team = 'TEAM_1'
SET @computer = 'LAPTOP'
END
IF @position = 'IT'
BEGIN
SET @team = 'TEAM_2'
SET @computer = 'DESKTOP'
END
IF @experience = 'JUNIOR'
BEGIN
SET @wfh = '0'
END
IF @experience = 'SENIOR'
BEGIN
SET @wfh = '1'
END
INSERT INTO @new_employee VALUES (@id, @wfh, @team, @computer)
RETURN
END
SELECT * FROM new_employee_hired('123456789', 'DEVELOPER', 'SENIOR');;
Résultat¶
ID_EMPLOYEE |
WORKING_FROM_HOME |
TEAM |
COMPUTER |
|---|---|---|---|
123456789 |
1 |
TEAM_1 |
LAPTOP |
Avertissement
Dans le cas d’instructions if imbriquées et si plusieurs variables sont modifiées dans les instructions, il est nécessaire d’utiliser une procédure stockée.
Mise à jour des valeurs précédemment insérées¶
Met à jour les valeurs des colonnes de la table dans le corps de la fonction et la renvoie avec les nouvelles valeurs.
Transact-SQL¶
MULTI-STATEMENT TABLE-VALUED¶
CREATE OR ALTER FUNCTION get_employees_history()
RETURNS @employee_history TABLE (
department_name NVARCHAR(50),
first_name NVARCHAR(50),
last_name NVARCHAR(50),
start_date DATE,
end_date DATE,
job_title NVARCHAR(50),
months_working INT
)
BEGIN
INSERT INTO @employee_history
SELECT D.name AS department_name, P.first_name, P.last_name, EH.start_date, EH.end_date, E.job_title, 0 FROM Department D
LEFT OUTER JOIN employee_department_history EH
ON D.department_ID = EH.department_ID
INNER JOIN Employee E
ON E.business_entity_ID = EH.business_entity_ID
INNER JOIN Person P
ON P.business_entity_ID = E.business_entity_ID
UPDATE @employee_history
SET
months_working =
CASE WHEN end_date IS NULL THEN DATEDIFF(MONTH, start_date, GETDATE())
ELSE DATEDIFF(MONTH, start_date, end_date)
END
RETURN;
END;
SELECT TOP(10) * FROM get_employees_history();
Résultat¶
DEPARTMENT_NAME |
FIRST_NAME |
LAST_NAME |
START_DATE |
END_DATE |
JOB_TITLE |
MONTHS_WORKING |
|---|---|---|---|---|---|---|
Sales |
Syed |
Abbas |
2013-03-14 |
NULL |
Directeur des ventes Pacifique |
106 |
Production |
Kim |
Abercombie |
2010-01-16 |
NULL |
Technicienne de production - WC60 |
144 |
Assurance qualité |
Hazem |
Abolrous |
2009-02-28 |
NULL |
Manager de l’assurance qualité |
155 |
Expédition et réception |
Pilar |
Ackerman |
2009-01-02 |
NULL |
Supervision des livraisons et des réceptions |
156 |
Production |
Jay |
Adams |
2009-03-05 |
NULL |
Technicienne de production - WC60 |
154 |
Services d’information |
François |
Ajenstat |
2009-01-17 |
NULL |
Administrateur de la base de données |
156 |
Sales |
Amy |
Alberts |
2012-04-16 |
NULL |
Directrice des ventes européennes |
17 |
Production |
Greg |
Alderson |
2008-12-02 |
NULL |
Technicienne de production - WC45 |
157 |
Assurance qualité |
Sean |
Alexander |
2008-12-28 |
NULL |
Technicien d’assurance qualité |
157 |
Installations et entretien |
Gary |
Altman |
2009-12-02 |
NULL |
Gestionnaire des ressources |
145 |
Snowflake SQL¶
MULTI-STATEMENT TABLE-VALUED¶
!!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'TABLE VALUED FUNCTIONS' NODE ***/!!!
CREATE OR ALTER FUNCTION get_employees_history ()
RETURNS EMPLOYEE_HISTORY TABLE (
department_name VARCHAR(50),
first_name VARCHAR(50),
last_name VARCHAR(50),
start_date DATE,
end_date DATE,
job_title VARCHAR(50),
months_working INT
)
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
BEGIN
INSERT INTO @employee_history
SELECT D.name AS department_name, P.first_name, P.last_name, EH.start_date, EH.end_date, E.job_title, 0 FROM Department D
LEFT OUTER JOIN employee_department_history EH
ON D.department_ID = EH.department_ID
INNER JOIN Employee E
ON E.business_entity_ID = EH.business_entity_ID
INNER JOIN Person P
ON P.business_entity_ID = E.business_entity_ID
UPDATE @employee_history
SET
months_working =
CASE WHEN end_date IS NULL THEN DATEDIFF(MONTH, start_date, GETDATE())
ELSE DATEDIFF(MONTH, start_date, end_date)
END
RETURN;
END;
SELECT TOP(10) * FROM get_employees_history();;
Résultat¶
DEPARTMENT_NAME |
FIRST_NAME |
LAST_NAME |
START_DATE |
END_DATE |
JOB_TITLE |
MONTHS_WORKING |
|---|---|---|---|---|---|---|
Sales |
Syed |
Abbas |
2013-03-14 |
NULL |
Directeur des ventes Pacifique |
106 |
Production |
Kim |
Abercombie |
2010-01-16 |
NULL |
Technicienne de production - WC60 |
144 |
Assurance qualité |
Hazem |
Abolrous |
2009-02-28 |
NULL |
Manager de l’assurance qualité |
155 |
Expédition et réception |
Pilar |
Ackerman |
2009-01-02 |
NULL |
Supervision des livraisons et des réceptions |
156 |
Production |
Jay |
Adams |
2009-03-05 |
NULL |
Technicienne de production - WC60 |
154 |
Services d’information |
François |
Ajenstat |
2009-01-17 |
NULL |
Administrateur de la base de données |
156 |
Sales |
Amy |
Alberts |
2012-04-16 |
NULL |
Directrice des ventes européennes |
17 |
Production |
Greg |
Alderson |
2008-12-02 |
NULL |
Technicienne de production - WC45 |
157 |
Assurance qualité |
Sean |
Alexander |
2008-12-28 |
NULL |
Technicien d’assurance qualité |
157 |
Installations et entretien |
Gary |
Altman |
2009-12-02 |
NULL |
Gestionnaire des ressources |
145 |
Clauses de retour multiples¶
Dans l’échantillon suivant, il y a plus d’une clause de retour, car selon la situation, il n’est pas nécessaire de continuer à exécuter toute la fonction.
Transact-SQL¶
MULTI-STATEMENT TABLE-VALUED¶
CREATE OR ALTER FUNCTIONcreate_new_team(@team_name VARCHAR(50))
</strong>RETURNS @new_team TABLE (type VARCHAR(50), name VARCHAR(50))
AS
BEGIN
DECLARE @employees INT
SET @employees = (SELECT count(*) FROM employee)
DECLARE @type VARCHAR(15)
SET @type = 'small_team'
IF (@employees < 8)
BEGIN
INSERT @new_team VALUES (@type, @team_name)
RETURN
END
SET @type = 'big_team'
INSERT @new_team VALUES (@type, @team_name)
RETURN
END
SELECT * FROMcreate_new_team('Team1');
Résultat¶
TYPE |
NAME |
|---|---|
SMALL_TEAM |
TEAM1 |
Snowflake SQL¶
MULTI-STATEMENT TABLE-VALUED¶
!!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'TABLE VALUED FUNCTIONS' NODE ***/!!!
CREATE OR ALTER FUNCTIONcreate_new_team (TEAM_NAME STRING)
RETURNS NEW_TEAM TABLE (
type VARCHAR(50),
name VARCHAR(50))
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
BEGIN
DECLARE @employees INT
SET @employees = (SELECT count(*) FROM employee)
DECLARE @type VARCHAR(15)
SET @type = 'small_team'
IF (@employees < 8)
BEGIN
INSERT @new_team VALUES (@type, @team_name)
RETURN
END
SET @type = 'big_team'
INSERT @new_team VALUES (@type, @team_name)
RETURN
END
SELECT * FROMcreate_new_team('Team1');;
Résultat¶
TYPE |
NAME |
|---|---|
SMALL_TEAM |
TEAM1 |
Avertissement
Cette transformation est appliquée lorsqu’il n’y a qu’une seule valeur à insérer. S’il y a plus d’une valeur, il est nécessaire d’utiliser une procédure stockée.
Cas complexes¶
L’exemple est un casse complexe qui utilise des instructions imbriquées if et insère une valeur en fonction de la condition true.
Transact-SQL¶
MULTI-STATEMENT TABLE-VALUED¶
CREATE OR ALTER FUNCTION vacation_status(@id VARCHAR (50))
RETURNS @status TABLE (vacation_status VARCHAR(30))
AS
BEGIN
DECLARE @hire_date DATETIME
SET @hire_date = (SELECT @hire_date FROM employee WHERE employeeId = @id)
DECLARE @vacation_hours INT
SET @vacation_hours = (SELECT count(vacation_hours) FROM employee WHERE employeeId = @id)
DECLARE @time_working INT
SET @time_working = (SELECT DATEDIFF(MONTH, @hire_date,GETDATE()))
IF (@vacation_hours > 0)
BEGIN
IF (@time_working > 3)
BEGIN
IF (@vacation_hours < 120)
BEGIN
INSERT INTO @status VALUES ('Ok')
END
IF (@vacation_hours = 120)
BEGIN
INSERT INTO @status values ('In the limit')
END
IF (@vacation_hours > 120)
BEGIN
INSERT INTO @status VALUES ('With excess')
END
END
ELSE
BEGIN
INSERT INTO @status values ('Hired recently')
END
END
ELSE
BEGIN
INSERT INTO @status values ('No hours')
END
RETURN
END
SELECT * FROM vacation_status('adventure-worksken0')
Résultat¶
VACATION_STATUS |
|---|
OK |
Snowflake SQL¶
MULTI-STATEMENT TABLE-VALUED¶
!!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'TABLE VALUED FUNCTIONS' NODE ***/!!!
CREATE OR ALTER FUNCTION vacation_status (ID STRING)
RETURNS STATUS TABLE (
vacation_status VARCHAR(30))
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
BEGIN
DECLARE @hire_date DATETIME
SET @hire_date = (SELECT @hire_date FROM employee WHERE employeeId = @id)
DECLARE @vacation_hours INT
SET @vacation_hours = (SELECT count(vacation_hours) FROM employee WHERE employeeId = @id)
DECLARE @time_working INT
SET @time_working = (SELECT DATEDIFF(MONTH, @hire_date,GETDATE()))
IF (@vacation_hours > 0)
BEGIN
IF (@time_working > 3)
BEGIN
IF (@vacation_hours < 120)
BEGIN
INSERT INTO @status VALUES ('Ok')
END
IF (@vacation_hours = 120)
BEGIN
INSERT INTO @status values ('In the limit')
END
IF (@vacation_hours > 120)
BEGIN
INSERT INTO @status VALUES ('With excess')
END
END
ELSE
BEGIN
INSERT INTO @status values ('Hired recently')
END
END
ELSE
BEGIN
INSERT INTO @status values ('No hours')
END
RETURN
END
SELECT * FROM vacation_status('adventure-worksken0');
Deuxième onglet¶
VACATION_STATUS |
|---|
OK |
Problèmes connus¶
Instructions While avec requêtes en parallèle¶
Le problème de cet exemple est qu’il n’y a aucun moyen de transformer l’instruction while en CTE à l’intérieur de la clause WITH du select principal, ce qui nous oblige à transformer cette instruction en procédure stockée pour conserver la même logique.
Transact-SQL¶
MULTI-STATEMENT TABLE-VALUED¶
--Additional Params: -t JavaScript
CREATE OR ALTER FUNCTION get_group_name
(@department_id INT)
RETURNS @group_names TABLE (group_name VARCHAR(15))
AS
BEGIN
DECLARE @name VARCHAR(30) = 'Another Department'
WHILE @name = 'Another Department'
BEGIN
IF (@department_id < 3)
BEGIN
SET @name = 'engineering'
END
IF @department_id = 3
BEGIN
SET @name = 'Tool Design'
END
SELECT @department_id = @department_id / 3
END
INSERT @group_names SELECT @name
RETURN
END
SELECT * FROM get_group_name(9);
Résultat¶
GROUP_NAME |
|---|
Conception d’outils |
Snowflake SQL¶
MULTI-STATEMENT TABLE-VALUED¶
!!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'TABLE VALUED FUNCTIONS' NODE ***/!!!
CREATE OR ALTER FUNCTION get_group_name
(DEPARTMENT_ID INT)
RETURNS @group_names TABLE (
group_name VARCHAR(15))
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
BEGIN
DECLARE @name VARCHAR(30) = 'Another Department'
WHILE @name = 'Another Department'
BEGIN
IF (@department_id < 3)
BEGIN
SET @name = 'engineering'
END
IF @department_id = 3
BEGIN
SET @name = 'Tool Design'
END
SELECT @department_id = @department_id / 3
END
INSERT @group_names SELECT @name
RETURN
END
SELECT * FROM get_group_name(9);;
Résultat¶
GROUP_NAME |
|---|
Conception d’outils |
Déclarer le curseur¶
Les fonctions définies par l’utilisateur ne peuvent pas DECLARE, OPEN, FETCH, CLOSE ou DEALLOCATE a CURSOR. Utilisez une procédure stockée pour travailler avec des curseurs.
Transact-SQL¶
MULTI-STATEMENT TABLE-VALUED¶
--Additional Params: -t JavaScript
CREATE OR ALTER FUNCTION amount_new_specimens(@id int)
RETURNS @new_specimens TABLE (amount int)
AS
BEGIN
DECLARE @first_specimen VARCHAR(30) ;
set @first_specimen = (select name_specimen from specimen where specimen_id = @id);
DECLARE @second_specimen VARCHAR(30);
DECLARE @specimens TABLE (name_specimen VARCHAR(30))
DECLARE Cursor1 CURSOR
FOR SELECT name_specimen
FROM specimen
OPEN cursor1
FETCH NEXT FROM cursor1
INTO @second_specimen;
WHILE @@FETCH_STATUS = 0
BEGIN
IF @first_specimen <> @second_specimen
BEGIN
INSERT INTO @specimens values (CONCAT_WS('-', @first_specimen, @second_specimen))
END
FETCH NEXT FROM cursor1
INTO @second_specimen;
END
CLOSE cursor1;
DEALLOCATE cursor1;
INSERT INTO @new_specimens SELECT COUNT(*) FROM @specimens
RETURN
END
SELECT * FROM amount_new_specimens(1);
Résultat¶
AMOUNT |
|---|
3 |
Snowflake SQL¶
MULTI-STATEMENT TABLE-VALUED¶
--Additional Params: -t JavaScript
!!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'TABLE VALUED FUNCTIONS' NODE ***/!!!
CREATE OR ALTER FUNCTION amount_new_specimens (ID INT)
RETURNS @new_specimens TABLE (
amount INT
)
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
BEGIN
DECLARE @first_specimen VARCHAR(30) ;
set @first_specimen = (select name_specimen from specimen where specimen_id = @id);
DECLARE @second_specimen VARCHAR(30);
DECLARE @specimens TABLE (name_specimen VARCHAR(30))
DECLARE Cursor1 CURSOR
FOR SELECT name_specimen
FROM specimen
OPEN cursor1
FETCH NEXT FROM cursor1
INTO @second_specimen;
WHILE @@FETCH_STATUS = 0
BEGIN
IF @first_specimen <> @second_specimen
BEGIN
INSERT INTO @specimens values (CONCAT_WS('-', @first_specimen, @second_specimen))
END
FETCH NEXT FROM cursor1
INTO @second_specimen;
END
CLOSE cursor1;
DEALLOCATE cursor1;
INSERT INTO @new_specimens SELECT COUNT(*) FROM @specimens
RETURN
END
SELECT * FROM amount_new_specimens(1);;
Résultat¶
AMOUNT |
|---|
3 |
Les instructions différentes ne sont pas prises en charge dans les expressions de tables communes¶
Les clauses UPDATE, INSERT, DELETE, ALTER ou DROP ne sont pas prises en charge dans le corps des expressions de tables communes, même après leur déclaration à l’aide d’un délimiteur. C’est pourquoi la fonction peut être modifiée pour fonctionner comme une procédure stockée.
Transact-SQL¶
MULTI-STATEMENT TABLE-VALUED¶
--Additional Params: -t JavaScript
CREATE OR ALTER PROCEDURE product_history
AS
BEGIN
DECLARE @product_history TABLE (
product_name NVARCHAR(50),
rating INT
)
INSERT INTO @product_history
SELECT P.Name AS product_name, AVG(ALL R.rating) FROM Production.product P
INNER JOIN Production.product_review R
ON R.product_ID = P.product_ID
GROUP BY P.Name;
DELETE FROM @product_history
WHERE rating < 2;
SELECT * FROM @product_history;
END
GO;
EXEC product_history
Résultat¶
PRODUCT_NAME |
Évaluation |
|---|---|
HL Mountain Pedal |
3 |
Mountain Bike Socks, M |
5 |
Road-550-W Yellow, 40 |
5 |
Snowflake SQL¶
MULTI-STATEMENT TABLE-VALUED¶
CREATE OR REPLACE PROCEDURE product_history ()
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 = 'UNKNOWN', 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(`CREATE OR REPLACE TEMPORARY TABLE T_product_history (
product_name VARCHAR(50),
rating INT
)`);
EXEC(` INSERT INTO T_product_history
SELECT
P.Name AS product_name,
AVG(ALL R.rating) FROM
Production.product P
INNER JOIN
Production.product_review R
ON R.product_ID = P.product_ID
GROUP BY
P.Name`);
EXEC(`DELETE FROM
T_product_history
WHERE
rating < 2`);
EXEC(`
SELECT
*
FROM
T_product_history`);
$$;
;
CALL product_history();
Résultat¶
PRODUCT_NAME |
Évaluation |
|---|---|
HL Mountain Pedal |
3 |
Mountain Bike Socks, M |
5 |
Road-550-W Yellow, 40 |
5 |
EWIs connexes¶
SSC-EWI-0040 : Instruction non prise en charge.
SSC-EWI-0073 : En attente de l’examen de l’équivalence fonctionnelle
SCALAR¶
Référence de traduction pour convertir une UDF Transact-SQL (Fonctions définies par l’utilisateur) avec type de retour scalaire en Snowflake.
Applies to
SQL Server
Azure Synapse Analytics
Description¶
Note
Certaines parties du code de sortie sont omises pour des raisons de clarté.
Une fonction scalaire définie par l’utilisateur est une routine Transact-SQL ou de runtime de langage commun (CLR) qui accepte des paramètres, exécute une action, telle qu’un calcul complexe, et renvoie le résultat de cette action sous forme de valeur scalaire. (Sous-section ReferenceCREATE FUNCTION de langage de serveur SQL).
Note
Ces fonctions sont généralement utilisées dans l’instruction SELECT ou dans la configuration d’une seule variable (très probablement dans une procédure stockée).
Syntaxe Transact-SQL¶
-- Transact-SQL Scalar Function Syntax
CREATE [ OR ALTER ] FUNCTION [ schema_name. ] function_name
( [ { @parameter_name [ AS ][ type_schema_name. ] parameter_data_type
[ = default ] [ READONLY ] }
[ ,...n ]
]
)
RETURNS return_data_type
[ WITH <function_option> [ ,...n ] ]
[ AS ]
BEGIN
function_body
RETURN scalar_expression
END
[ ; ]
Syntaxe Snowflake¶
Snowflake permet l’utilisation de 3 langues différentes dans les fonctions définies par l’utilisateur :
SQL
JavaScript
Java
Pour l’instant, SnowConvert AI prendra en charge uniquement SQL et JavaScript comme langages cibles.
SQL¶
Note
Les fonctions définies par l’utilisateur SQL n’acceptent qu’une seule requête comme corps. Elles peuvent lire la base de données, mais ne sont pas autorisées à l’écrire ou à la modifier. (Référence des UDFs SQL scalaires).
CREATE [ OR REPLACE ] [ SECURE ] FUNCTION <name> ( [ <arg_name> <arg_data_type> ] [ , ... ] )
RETURNS { <result_data_type> | TABLE ( <col_name> <col_data_type> [ , ... ] ) }
[ [ NOT ] NULL ]
[ { CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT } } ]
[ VOLATILE | IMMUTABLE ]
[ COMMENT = '<string_literal>' ]
AS '<function_definition>'
JavaScript¶
Note
Les fonctions définies par l’utilisateur JavaScript autorisent plusieurs instructions dans leur corps, mais ne peuvent pas effectuer de requêtes dans la base de données. (Référence des UDFs JavaScript scalaires)
CREATE [ OR REPLACE ] [ SECURE ] FUNCTION <name> ( [ <arg_name> <arg_data_type> ] [ , ... ] )
RETURNS { <result_data_type> | TABLE ( <col_name> <col_data_type> [ , ... ] ) }
[ [ NOT ] NULL ]
LANGUAGE JAVASCRIPT
[ { CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT } } ]
[ VOLATILE | IMMUTABLE ]
[ COMMENT = '<string_literal>' ]
AS '<function_definition>'
Modèles d’échantillons de sources¶
Instructions Set et Declare¶
Les instructions les plus courantes dans les corps de fonctions sont les instructions DECLARE et SET. Pour les instructions DECLARE sans valeur par défaut, la transformation sera ignorée. Les instructions SET et DECLARE avec une valeur par défaut seront transformées en COMMON TABLE EXPRESSION. Chaque expression de table commune contiendra une colonne qui représente la valeur de la variable locale.
Transact-SQL¶
Requête¶
CREATE OR ALTER FUNCTION PURCHASING.GetVendorName()
RETURNS NVARCHAR(50) AS
BEGIN
DECLARE @result NVARCHAR(50)
DECLARE @BUSINESSENTITYID INT
SET @BUSINESSENTITYID = 1492
SELECT @result = Name FROM PURCHASING.VENDOR WHERE BUSINESSENTITYID = @BUSINESSENTITYID
RETURN @result
END
GO
SELECT PURCHASING.GetVendorName() as vendor_name;
Résultat¶
vendor_name |
|---|
Australia Bike Retailer |
Snowflake¶
Requête¶
CREATE OR REPLACE FUNCTION PURCHASING.GetVendorName ()
RETURNS VARCHAR(50)
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
$$
WITH CTE1 AS
(
SELECT
1492 AS BUSINESSENTITYID
),
CTE2 AS
(
SELECT
Name AS RESULT
FROM
PURCHASING.VENDOR
WHERE
BUSINESSENTITYID = (
SELECT
BUSINESSENTITYID
FROM
CTE1
)
)
SELECT
RESULT
FROM
CTE2
$$;
SELECT
PURCHASING.GetVendorName() as vendor_name;
Résultat¶
VENDOR_NAME |
|---|
Australia Bike Retailer |
Transformation des instructions If/Else¶
Les instructions If/Else peuvent être traitées de différentes manières, elles peuvent être transformées en Javascript ou en SQL à l’aide de CASE EXPRESSION dans Select qui permet des conditionnels à l’intérieur des requêtes, alors que la transformation en Javascript est assez simple, l’instruction Case peut ne pas être si évidente à première vue.
Transact-SQL¶
Requête¶
CREATE OR ALTER FUNCTION PURCHASING.HasActiveFlag(@BusinessEntityID int)
RETURNS VARCHAR(10) AS
BEGIN
DECLARE @result VARCHAR(10)
DECLARE @ActiveFlag BIT
SELECT @ActiveFlag = ActiveFlag from PURCHASING.VENDOR v where v.BUSINESSENTITYID = @BusinessEntityID
IF @ActiveFlag = 1
SET @result = 'YES'
ELSE IF @ActiveFlag = 0
SET @result = 'NO'
RETURN @result
END
GO
SELECT PURCHASING.HasActiveFlag(1516) as has_active_flag;
Résultat¶
has_active_flag |
|---|
NO |
Snowflake¶
Requête¶
CREATE OR REPLACE FUNCTION PURCHASING.HasActiveFlag (P_BUSINESSENTITYID INT)
RETURNS VARCHAR(10)
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
$$
WITH CTE1 AS
(
SELECT
ActiveFlag AS ACTIVEFLAG
from
PURCHASING.VENDOR v
where
v.BUSINESSENTITYID = P_BUSINESSENTITYID
),
CTE2 AS
(
SELECT
CASE
WHEN (
SELECT
ACTIVEFLAG
FROM
CTE1
) = 1
THEN 'YES'
WHEN (
SELECT
ACTIVEFLAG
FROM
CTE1
) = 0
THEN 'NO'
END AS RESULT
)
SELECT
RESULT
FROM
CTE2
$$;
SELECT
PURCHASING.HasActiveFlag(1516) as has_active_flag;
Résultat¶
HAS_ACTIVE_FLAG |
|---|
NO |
Instructions imbriquées¶
Pour les instructions imbriquées, la programmation structurée est transformée en une requête unique. Les instructions du contrôle du flux seront imbriquées dans des structures de table afin de préserver l’ordre d’exécution.
Note
CASE EXPRESSIONS ne peut renvoyer qu’une seule valeur par instruction
Exemple¶
Note
Le code suivant est fonctionnellement équivalent dans les deux paradigmes de programmation.
Programmation structurée¶
DECLARE @VendorId AS int;
DECLARE @AccountNumber AS VARCHAR(50);
SELECT @VendorId = poh.VendorID
FROM Purchasing.PurchaseOrderHeader poh
WHERE PurchaseOrderID = 1
SELECT @AccountNumber = v.AccountNumber
FROM Purchasing.Vendor v
WHERE v.BusinessEntityID = @VendorId
SQL¶
SELECT V.AccountNumber AccountNumber
FROM (SELECT poh.VendorID VendorId
FROM Purchasing.PurchaseOrderHeader poh
WHERE PurchaseOrderID = 1
) T1, Purchasing.Vendor v
WHERE v.BusinessEntityID = T1.VendorId
Résultat¶
AccountNumber |
|---|
LITWARE0001 |
Variables conditionnelles via SELECTs¶
La définition et l’affectation de variables dans les instructions conditionnelles posent généralement des problèmes, car les références à la variable plus loin dans le code doivent savoir où la variable a été modifiée pour la dernière fois. De plus, si la référence se trouve à l’intérieur d’une autre instruction conditionnelle, il faudrait une sorte de redirection qui fasse référence à l’affectation précédente connue de la variable.
Le tout est aggravé par l’imbrication et les requêtes complexes qui peuvent être trouvées sur le code d’entrée. C’est pourquoi un EWI est ajouté lorsque ces modèles sont trouvés.
Dans le scénario suivant, la première instruction IF peut être transformée sans problème, car son contenu est assez simple. Les deuxième et troisième instructions IF sont commentées parce qu’elles ne sont pas prises en charge pour le moment, étant donné qu’il existe des instructions autres que les affectations de variables par le biais de SELECT.
SQL Server¶
Requête¶
CREATE or ALTER FUNCTION PURCHASING.SELECTINUDF (
@param1 varchar(12)
)
RETURNS int
AS
BEGIN
declare @var1 int;
declare @var2 int;
declare @var3 int;
IF @param1 = 'first'
BEGIN
select @var1 = col1 + 10 from table1 WHERE id = 0;
select @var2 = col1 + 20 from table1 WHERE id = 0;
select @var3 = col1 + 30 from table1 WHERE id = 0;
END
IF @param1 = 'second'
BEGIN
declare @var4 int = 10;
select @var1 = col1 + 40 from table1 WHERE id = 0;
select @var2 = col1 + 40 from table1 WHERE id = 0;
END
IF @param1 = 'third'
BEGIN
select col1 from table1 where id = 0;
select @var1 = col1 + 50 from table1 WHERE id = 0;
select @var2 = col1 + 50 from table1 WHERE id = 0;
END
RETURN @var1
END
SELECT PURCHASING.SELECTINUDF('first') as result; -- Assuming table1.col1 is 0 when ID = 0
Résultat¶
RESULT |
|---|
10 |
Snowflake¶
Requête¶
CREATE OR REPLACE FUNCTION PURCHASING.SELECTINUDF (PARAM1 STRING)
RETURNS INT
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
$$
WITH CTE1 AS
(
SELECT
CASE
WHEN PARAM1 = 'first'
THEN (SELECT
col1 + 10 AS VAR1 from
table1
WHERE
id = 0)
END AS VAR1,
CASE
WHEN PARAM1 = 'first'
THEN (SELECT
col1 + 20 AS VAR2 from
table1
WHERE
id = 0)
END AS VAR2,
CASE
WHEN PARAM1 = 'first'
THEN (SELECT
col1 + 30 AS VAR3 from
table1
WHERE
id = 0)
END AS VAR3
),
!!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'IF STATEMENT' NODE ***/!!!
CTE2 AS
(
/* IF @param1 = 'second'
BEGIN
declare @var4 int = 10;
select @var1 = col1 + 40 from table1 WHERE id = 0;
select @var2 = col1 + 40 from table1 WHERE id = 0;
END*/
SELECT
null
),
!!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'IF STATEMENT' NODE ***/!!!
CTE3 AS
(
/* IF @param1 = 'third'
BEGIN
select col1 from table1 where id = 0;
select @var1 = col1 + 50 from table1 WHERE id = 0;
select @var2 = col1 + 50 from table1 WHERE id = 0;
END*/
SELECT
null
),
CTE4 AS
(
SELECT
PURCHASING.SELECTINUDF('first') as result
)
SELECT
VAR1
FROM
CTE4
$$ -- Assuming table1.col1 is 0 when ID = 0
;
Résultat¶
RESULT |
|---|
10 |
Assigner et retourner une variable¶
Dans ce modèle simple, il y a une déclaration de variable, puis cette variable est définie à l’aide d’une instruction SELECT et enfin renvoyée. Celle-ci va être migrée vers une expression de table commune afin de conserver le comportement d’origine.
SQL Server¶
Requête¶
CREATE OR ALTER FUNCTION Purchasing.GetTotalFreight()
RETURNS MONEY AS
BEGIN
DECLARE @Result MONEY
SELECT @Result = ISNULL(SUM(t.Freight), 0) from Purchasing.PurchaseOrderHeader t
return @Result
END
GO
select Purchasing.GetTotalFreight() as Result;
Résultat¶
Résultat |
|---|
1583978.2263 |
Snowflake¶
Requête¶
CREATE OR REPLACE FUNCTION Purchasing.GetTotalFreight ()
RETURNS NUMBER(38, 4)
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
$$
WITH CTE1 AS
(
SELECT
NVL(SUM(t.Freight), 0) AS RESULT from
Purchasing.PurchaseOrderHeader t
)
SELECT
RESULT
FROM
CTE1
$$;
select
Purchasing.GetTotalFreight() as Result;
Résultat¶
RESULT |
|---|
1583978.2263 |
Appels de fonctions multiples¶
Pour ce modèle spécifique, il n’y a pas de requêtes évidentes, mais il y a plusieurs appels à plusieurs fonctions travaillant sur la même variable et la renvoyant à la fin. Comme Snowflake ne prend en charge que les requêtes à l’intérieur de ses fonctions, la solution pour ce bloc va être de l’ajouter à un Select et d’imbriquer les appels à l’intérieur, en s’assurant que la valeur de retour est la même que celle de la source.
SQL Server¶
Requête¶
CREATE OR ALTER FUNCTION PURCHASING.Foo
(
@PARAM1 INT
)
RETURNS varchar(25)
AS
BEGIN
DECLARE @filter INT = @PARAM1
DECLARE @NAME VARCHAR(25) = (SELECT Name from Purchasing.Vendor v where BusinessEntityID = @filter)
SET @NAME = REPLACE(@NAME, 'Australia', 'USA')
SET @NAME = REPLACE(@NAME, 'Bike', 'Car')
RETURN @NAME
END
GO
SELECT PURCHASING.Foo(1492) AS Name;
Résultat¶
Nom |
|---|
USA Car Retailer |
Snowflake¶
Requête¶
CREATE OR REPLACE FUNCTION PURCHASING.Foo (PARAM1 INT)
RETURNS VARCHAR(25)
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
$$
WITH CTE1 AS
(
SELECT
PARAM1 AS FILTER
),
CTE2 AS
(
SELECT
(SELECT
Name
from
Purchasing.Vendor v
where
BusinessEntityID = (
SELECT
FILTER
FROM
CTE1
)
) AS NAME
),
CTE3 AS
(
SELECT
REPLACE((
SELECT
NAME
FROM
CTE3
), 'Australia', 'USA') AS NAME
),
CTE4 AS
(
SELECT
REPLACE((
SELECT
NAME
FROM
CTE4
), 'Bike', 'Car') AS NAME
)
SELECT
NAME
FROM
CTE4
$$;
SELECT
PURCHASING.Foo(1492) AS Name;
Résultat¶
NAME |
|---|
USA Car Retailer |
Augmenter une variable en fonction de plusieurs conditions IF et renvoyer sa valeur¶
Pour ce modèle, une variable est modifiée (augmentée dans le cas présent) à l’aide de plusieurs conditions IF. Au début, un jeu de résultats est initialisé et utilisé pour déterminer si la variable de résultat doit être augmentée ou non. Enfin, la variable de résultat est renvoyée.
SQL Server¶
Requête¶
CREATE OR ALTER FUNCTION PURCHASING.FOO()
RETURNS MONEY
AS
BEGIN
declare @firstValue MONEY
declare @secondValue MONEY
declare @Result MONEY
select @Result = 0
select @firstValue = SubTotal from Purchasing.PurchaseOrderHeader where PurchaseOrderID = 1
select @secondValue = SubTotal from Purchasing.PurchaseOrderHeader where PurchaseOrderID = 2
if @firstValue is not null
select @Result = @Result + @firstValue
if @secondValue is not null
select @Result = @Result + @secondValue
return @Result
END
GO
SELECT PURCHASING.Foo() AS Result;
Résultat¶
Résultat |
|---|
473.1415 |
Snowflake¶
Requête¶
CREATE OR REPLACE FUNCTION PURCHASING.FOO ()
RETURNS NUMBER(38, 4)
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
$$
WITH CTE1 AS
(
select
0 AS RESULT
),
CTE2 AS
(
select
SubTotal AS FIRSTVALUE
from
Purchasing.PurchaseOrderHeader
where
PurchaseOrderID = 1
),
CTE3 AS
(
select
SubTotal AS SECONDVALUE
from
Purchasing.PurchaseOrderHeader
where
PurchaseOrderID = 2
),
CTE4 AS
(
SELECT
CASE
WHEN (
SELECT
FIRSTVALUE
FROM
CTE2
) is not null
THEN (
select
(
SELECT
RESULT
FROM
CTE1
) + (
SELECT
FIRSTVALUE
FROM
CTE2
) AS RESULT)
END AS RESULT
),
CTE5 AS
(
SELECT
CASE
WHEN (
SELECT
SECONDVALUE
FROM
CTE3
) is not null
THEN (
select
(
SELECT
RESULT
FROM
CTE1
) + (
SELECT
SECONDVALUE
FROM
CTE3
) AS RESULT)
ELSE (SELECT
RESULT
FROM
CTE4)
END AS RESULT
)
SELECT
RESULT
FROM
CTE5
$$;
SELECT
PURCHASING.Foo() AS Result;
Résultat¶
RESULT |
|---|
473.1415 |
Deux ou plusieurs instructions RETURN¶
Pour ce modèle, le bloc IF contenant la clause de retour qui interrompt le flux de code est ajouté à la fin du corps, comme la dernière instruction à exécuter dans une expression CASE.
Cas de base¶
Pour ce scénario particulier, il n’y a pas de logique entre l’instruction conditionnelle RETURN et l’instruction finale RETURN, de sorte que tous les corps seront mappés sur une seule CASE EXPRESSION.
SQL Server¶
Requête¶
CREATE OR ALTER FUNCTION [PURCHASING].[FOO] ()
RETURNS INT
AS
BEGIN
IF exists (SELECT PreferredVendorStatus FROM Purchasing.Vendor v )
RETURN 1
RETURN 0
END
GO
SELECT PURCHASING.FOO() as result;
Résultat¶
résultat |
|---|
1 |
Snowflake¶
Requête¶
CREATE OR REPLACE FUNCTION PURCHASING.FOO ()
RETURNS INT
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
$$
SELECT
CASE
WHEN exists (SELECT
PreferredVendorStatus
FROM
Purchasing.Vendor v
)
THEN 1
ELSE 0
END
$$;
SELECT
PURCHASING.FOO() as result;
Résultat¶
RESULT |
|---|
1 |
Expressions de table communes¶
Les expressions de table communes seront conservées telles quelles dans le code d’origine et seront concaténées avec celles qui seront générées. SnowConvert AI est capable d’identifier d’abord tous les noms COMMON TABLE EXPRESSION d’origine afin d’éviter de générer des noms en double.
SQL Server¶
Requête¶
CREATE OR ALTER FUNCTION [PURCHASING].[FOO]
(
@status INT
)
Returns INT
As
Begin
Declare @result as int = 0
;WITH ctetable(RevisionNumber) as
(
SELECT RevisionNumber
FROM Purchasing.PurchaseOrderHeader poh
where poh.Status = @status
),
finalCte As
(
SELECT RevisionNumber FROM ctetable
)
Select @result = count(RevisionNumber) from finalCte
return @result;
End
GO
SELECT PURCHASING.FOO(4) as result;
Résultat¶
résultat |
|---|
3689 |
Snowflake¶
Requête¶
CREATE OR REPLACE FUNCTION PURCHASING.FOO (STATUS INT)
Returns INT
LANGUAGE SQL
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},"attributes":{"component":"transact"}}'
AS
$$
WITH CTE1 AS
(
SELECT
0 AS RESULT
),
ctetable (
RevisionNumber
) as
(
SELECT
RevisionNumber
FROM
Purchasing.PurchaseOrderHeader poh
where
poh.Status = STATUS
),
finalCte As
(
SELECT
RevisionNumber
FROM
ctetable
),
CTE2 AS
(
Select
COUNT(RevisionNumber) AS RESULT from
finalCte
)
SELECT
RESULT
FROM
CTE2
$$;
SELECT
PURCHASING.FOO(4) as result;
Résultat¶
RESULT |
|---|
3689 |
Transformer en UDFs JavaScript¶
S’il y a plusieurs instructions et que la fonction n’accède en aucune façon à la base de données, elle peut être transformée en une fonction JavaScript en conservant l’équivalence fonctionnelle
SQL Server¶
Requête 1¶
CREATE OR ALTER FUNCTION PURCHASING.GetFiscalYear
(
@DATE AS DATETIME
)
RETURNS INT
AS
BEGIN
DECLARE @FiscalYear AS INT
DECLARE @CurMonth AS INT
SET @CurMonth = DATEPART(M,@DATE)
SET @FiscalYear = DATEPART(YYYY, @DATE)
IF (@CurMonth >= 7)
BEGIN
SET @FiscalYear = @FiscalYear + 1
END
RETURN @FiscalYear
END
GO
SELECT PURCHASING.GetFiscalYear('2020-10-10') as DATE;
Requête 2¶
CREATE OR ALTER FUNCTION PURCHASING.[getCleanChargeCode]
(
@ChargeCode varchar(50)
)
returns varchar(50) as
begin
declare @CleanChargeCode varchar(50),@Len int,@Pos int=2
set @Pos=LEN(@ChargeCode)-1
while @Pos > 1
begin
set @CleanChargeCode=RIGHT(@ChargeCode,@Pos)
if TRY_CAST(@CleanChargeCode as bigint) is not null
return @CleanChargeCode
set @Pos=@Pos-1
end
set @Pos=LEN(@ChargeCode)-1
while @Pos > 1
begin
set @CleanChargeCode=LEFT(@ChargeCode,@Pos)
if TRY_CAST(@CleanChargeCode as bigint) is not null
return @CleanChargeCode
set @Pos=@Pos-1
end
return null
end
GO
SELECT PURCHASING.[getCleanChargeCode]('16test') AS CleanChargeCode;
Résultat 1¶
DATE |
|---|
2021 |
Result 2¶
CleanChargeCode |
|---|
16 |
Snowflake¶
Requête 1¶
!!!RESOLVE EWI!!! /*** SSC-EWI-0068 - USER DEFINED FUNCTION WAS TRANSFORMED TO SNOWFLAKE PROCEDURE ***/!!!
CREATE OR REPLACE PROCEDURE PURCHASING.GetFiscalYear (DATE TIMESTAMP_NTZ(3))
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "transact", "convertedOn": "07/11/2025", "domain": "no-domain-provided" }}'
EXECUTE AS CALLER
AS
$$
DECLARE
FISCALYEAR INT;
CURMONTH INT;
BEGIN
CURMONTH := DATE_PART(month, :DATE :: TIMESTAMP);
FISCALYEAR := DATE_PART(year, :DATE :: TIMESTAMP);
IF ((:CURMONTH >= 7)) THEN
BEGIN
FISCALYEAR := :FISCALYEAR + 1;
END;
END IF;
RETURN :FISCALYEAR;
END;
$$;
SELECT
PURCHASING.GetFiscalYear('2020-10-10') !!!RESOLVE EWI!!! /*** SSC-EWI-0067 - UDF WAS TRANSFORMED TO SNOWFLAKE PROCEDURE, CALLING PROCEDURES INSIDE QUERIES IS NOT SUPPORTED ***/!!! as DATE;
Requête 2¶
!!!RESOLVE EWI!!! /*** SSC-EWI-0068 - USER DEFINED FUNCTION WAS TRANSFORMED TO SNOWFLAKE PROCEDURE ***/!!!
CREATE OR REPLACE PROCEDURE PURCHASING.getCleanChargeCode (CHARGECODE STRING)
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "transact", "convertedOn": "07/16/2025", "domain": "no-domain-provided" }}'
EXECUTE AS CALLER
AS
$$
DECLARE
CLEANCHARGECODE VARCHAR(50);
LEN INT;
POS INT := 2;
BEGIN
POS := LEN(:CHARGECODE)-1;
WHILE (:POS > 1) LOOP
CLEANCHARGECODE := RIGHT(:CHARGECODE, :POS);
IF (CAST(:CLEANCHARGECODE AS BIGINT) /*** SSC-FDM-TS0005 - TRY_CONVERT/TRY_CAST COULD NOT BE CONVERTED TO TRY_CAST ***/!!!RESOLVE EWI!!! /*** SSC-EWI-TS0074 - CAST RESULT MAY BE DIFFERENT FROM TRY_CAST FUNCTION DUE TO MISSING DEPENDENCIES ***/!!! is not null) THEN
RETURN :CLEANCHARGECODE;
END IF;
POS := :POS -1;
END LOOP;
POS := LEN(:CHARGECODE)-1;
WHILE (:POS > 1) LOOP
CLEANCHARGECODE := LEFT(:CHARGECODE, :POS);
IF (CAST(:CLEANCHARGECODE AS BIGINT) /*** SSC-FDM-TS0005 - TRY_CONVERT/TRY_CAST COULD NOT BE CONVERTED TO TRY_CAST ***/!!!RESOLVE EWI!!! /*** SSC-EWI-TS0074 - CAST RESULT MAY BE DIFFERENT FROM TRY_CAST FUNCTION DUE TO MISSING DEPENDENCIES ***/!!! is not null) THEN
RETURN :CLEANCHARGECODE;
END IF;
POS := :POS -1;
END LOOP;
RETURN null;
END;
$$;
SELECT
PURCHASING.getCleanChargeCode('16test') !!!RESOLVE EWI!!! /*** SSC-EWI-0067 - UDF WAS TRANSFORMED TO SNOWFLAKE PROCEDURE, CALLING PROCEDURES INSIDE QUERIES IS NOT SUPPORTED ***/!!! AS CleanChargeCode;
Résultat 1¶
DATE |
|---|
2021.0 |
Result 2¶
CLEANCHARGECODE |
|---|
16 |
Problèmes connus¶
Avertissement
Les fonctions définies par l’utilisateur ne peuvent pas être utilisées pour effectuer des actions qui modifient l’état de la base de données
Avertissement
Les fonctions définies par l’utilisateur ne peuvent pas contenir de clause OUTPUT INTO ayant une table pour cible
Avertissement
Les fonctions définies par l’utilisateur ne peuvent pas DECLARE, OPEN, FETCH, CLOSE ou DEALLOCATE a CURSOR. Utilisez une procédure stockée si vous devez utiliser des curseurs.
Avertissement
Les fonctions définies par l’utilisateur ne peuvent pas exécuter d’instructions de contrôle du flux, telles que WHILE, s’il y a au moins un appel à la base de données
Avertissement
Les fonctions définies par l’utilisateur qui contiennent des références à d’autres fonctions définies par l’utilisateur et qui ont été transformées en procédures stockées seront également transformées en procédures stockées.
Avertissement
Les fonctions définies par l’utilisateur qui utilisent @@ROWCOUNT ne sont pas prises en charge dans SQL et doivent être transformées en procédures stockées afin de conserver l’équivalence fonctionnelle.
Avertissement
Les fonctions définies par l’utilisateur qui ont des instructions SELECT assignant une variable à elle-même ne sont pas prises en charge dans Snowflake. Voir aussi SELECT @local_variable
Pour tous les cas non pris en charge, veuillez consulter les EWIs connexes et les modèles ci-dessous pour obtenir des recommandations et des solutions de contournement.
Conditionnelles autres que les instructions if/else en parallèle des requêtes¶
Le scénario suivant implique l’utilisation de l’instruction « while » en parallèle d’autres requêtes. Le problème de cet exemple est qu’il n’y a aucun moyen de transformer l’instruction while en CTE à l’intérieur de la clause WITH du select principal, ce qui nous oblige à transformer cette instruction en procédure JavaScript pour conserver la même logique.
SQL Server¶
Requête¶
CREATE OR ALTER FUNCTION PURCHASING.FOO()
RETURNS INT
AS
BEGIN
DECLARE @i int = 0, @p int;
Select @p = COUNT(*) FROM PURCHASING.VENDOR
WHILE (@p < 1000)
BEGIN
SET @i = @i + 1
SET @p = @p + @i
END
IF (@i = 6)
RETURN 1
RETURN @p
END
GO
SELECT PURCHASING.FOO() as result;
Résultat¶
résultat |
|---|
1007 |
Snowflake
Requête¶
!!!RESOLVE EWI!!! /*** SSC-EWI-0068 - USER DEFINED FUNCTION WAS TRANSFORMED TO SNOWFLAKE PROCEDURE ***/!!!
CREATE OR REPLACE PROCEDURE PURCHASING.FOO ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "transact", "convertedOn": "07/11/2025", "domain": "no-domain-provided" }}'
EXECUTE AS CALLER
AS
$$
DECLARE
I INT := 0;
P INT;
BEGIN
Select
COUNT(*)
INTO
:P
FROM
PURCHASING.VENDOR;
WHILE (:P < 1000) LOOP
I := :I + 1;
P := :P + :I;
END LOOP;
IF ((:I = 6)) THEN
RETURN 1;
END IF;
RETURN :P;
END;
$$;
SELECT
PURCHASING.FOO() !!!RESOLVE EWI!!! /*** SSC-EWI-0067 - UDF WAS TRANSFORMED TO SNOWFLAKE PROCEDURE, CALLING PROCEDURES INSIDE QUERIES IS NOT SUPPORTED ***/!!! as result;
Résultat¶
FOO |
|---|
1007 |
Affectation d’une variable à l’aide de sa propre valeur en parcourant un jeu de lignes¶
Dans l’exemple suivant, la variable @names est utilisée pour concaténer plusieurs valeurs d’une colonne en une seule chaîne. La variable est mise à jour à chaque itération comme indiqué, ce qui n’est pas pris en charge par les UDFs SnowFlake. Pour ce scénario, la fonction doit être transformée en procédure.
SQL Server
Requête¶
CREATE OR ALTER FUNCTION PURCHASING.FOO()
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE @names varchar(8000)
SET @names = ''
SELECT @names = ISNULL(@names + ' ', '') + Name from Purchasing.Vendor v
return @names
END
GO
select PURCHASING.FOO() as names;
Résultat¶
names |
|---|
Australia Bike Retailer Allenson Cycles Advanced Bicycles Trikes, Inc. Morgan Bike Accessories Cycling Master Chicago Rent-All Greenwood Athletic Company Compete Enterprises, Inc International Light Speed Training Systems Gardner Touring Cycles Internati |
Requête Snowflake
!!!RESOLVE EWI!!! /*** SSC-EWI-0068 - USER DEFINED FUNCTION WAS TRANSFORMED TO SNOWFLAKE PROCEDURE ***/!!!
CREATE OR REPLACE PROCEDURE PURCHASING.FOO ()
RETURNS VARCHAR
LANGUAGE SQL
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "transact", "convertedOn": "07/11/2025", "domain": "no-domain-provided" }}'
EXECUTE AS CALLER
AS
$$
DECLARE
NAMES VARCHAR(8000);
BEGIN
NAMES := '';
SELECT
NVL(:NAMES || ' ', '') + Name
INTO
:NAMES
from
Purchasing.Vendor v;
RETURN :NAMES;
END;
$$;
select
PURCHASING.FOO() !!!RESOLVE EWI!!! /*** SSC-EWI-0067 - UDF WAS TRANSFORMED TO SNOWFLAKE PROCEDURE, CALLING PROCEDURES INSIDE QUERIES IS NOT SUPPORTED ***/!!! as names;
Avertissement
Pour les scénarios décrits ci-dessus, tenez compte des limites suivantes :
Tous les appels aux fonctions définies par l’utilisateur dans les requêtes DML telles que
SELECT,INSERT,DELETE,UPDATEouMERGEéchoueront parce que les appels aux procédures stockées dans ces requêtes ne sont pas autorisés.Les appels à des fonctions définies par l’utilisateur à l’intérieur de procédures doivent être précédés du mot-clé
CALL.Les fonctions définies par l’usage utilisées dans les COMPUTED COLUMNS échoueront lors de l’exécution.
EWIs connexes¶
SSC-EWI-0067 : UDF a été transformée en procédure Snowflake, l’appel de procédures à l’intérieur d’une requête n’est pas pris en charge.
SSC-EWI-0068 : La fonction définie par l’utilisateur a été transformée en procédure Snowflake.
SSC-EWI-0073 : En attente de l’examen de l’équivalence fonctionnelle.
Script Snowflake UDF (SCALAR)¶
Référence de traduction pour les fonctions scalaires définies par l’utilisateur du serveur SQL vers les UDFs Exécution de scripts Snowflake
Applies to
SQL Server
Azure Synapse Analytics
Description¶
SnowConvert prend en charge la traduction des fonctions scalaires définies par l’utilisateur du serveur SQL directement vers les UDFs Exécution de scripts Snowflake (SnowScript UDFs) lorsqu’elles répondent à des critères spécifiques, au lieu de convertir toutes les fonctions en procédures stockées.
Les UDFs Exécution de scripts Snowflake sont des fonctions définies par l’utilisateur écrites à l’aide de la syntaxe du langage procédural de Snowflake (Snowscript) au sein d’un corps SQL UDF. Elles prennent en charge les variables, les boucles, la logique conditionnelle et le traitement des exceptions.
Lorsque les fonctions deviennent des SnowScript UDFs¶
SnowConvert analyse chaque fonction de serveur SQL et détermine automatiquement la cible Snowflake appropriée. Une fonction devient une SnowScript UDF lorsqu’elle contient uniquement une logique procédurale sans opérations d’accès aux données.
Modèles d’échantillons de sources¶
Fonction de calcul simple¶
Une fonction scalaire de base qui effectue des calculs sans interroger de données.
SQL Server¶
CREATE FUNCTION dbo.CalculateProfit
(
@Cost DECIMAL(10,2),
@Revenue DECIMAL(10,2)
)
RETURNS DECIMAL(10,2)
AS
BEGIN
DECLARE @Profit DECIMAL(10,2)
SET @Profit = @Revenue - @Cost
RETURN @Profit
END
GO
SELECT dbo.CalculateProfit(100.00, 150.00) as Profit;
Résultat¶
Bénéfice |
|---|
50,00 |
Snowflake (UDF SnowScript)¶
CREATE OR REPLACE FUNCTION dbo.CalculateProfit (COST DECIMAL(10,2), REVENUE DECIMAL(10,2))
RETURNS DECIMAL(10, 2)
LANGUAGE SQL
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "transact", "convertedOn": "10/09/2025", "domain": "no-domain-provided", "migrationid": "QsqZARsvG3aeleeXZB43fg==" }}'
AS
$$
DECLARE
PROFIT DECIMAL(10, 2);
BEGIN
PROFIT := :REVENUE - :COST;
RETURN :PROFIT;
END;
$$;
SELECT
dbo.CalculateProfit(100.00, 150.00) as Profit;
Résultat¶
PROFIT |
|---|
50,00 |
Fonction avec logique conditionnelle (IF/ELSE)¶
Fonctions utilisant les instructions IF/ELSE pour la logique métier.
SQL Server¶
CREATE FUNCTION dbo.GetDiscountRate
(
@CustomerType VARCHAR(20),
@OrderAmount DECIMAL(10,2)
)
RETURNS DECIMAL(5,2)
AS
BEGIN
DECLARE @Discount DECIMAL(5,2)
IF @CustomerType = 'Premium'
SET @Discount = 0.15
ELSE IF @CustomerType = 'Standard'
SET @Discount = 0.10
ELSE
SET @Discount = 0.05
IF @OrderAmount > 1000
SET @Discount = @Discount + 0.05
RETURN @Discount
END
GO
SELECT dbo.GetDiscountRate('Premium', 1200.00) as DiscountRate;
Résultat¶
DiscountRate |
|---|
0.20 |
Snowflake (UDF SnowScript)¶
CREATE OR REPLACE FUNCTION dbo.GetDiscountRate (CUSTOMERTYPE STRING, ORDERAMOUNT DECIMAL(10,2))
RETURNS DECIMAL(5, 2)
LANGUAGE SQL
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "transact", "convertedOn": "10/09/2025", "domain": "no-domain-provided", "migrationid": "QsqZARsvG3aeleeXZB43fg==" }}'
AS
$$
DECLARE
DISCOUNT DECIMAL(5, 2);
BEGIN
IF (:CUSTOMERTYPE = 'Premium') THEN
DISCOUNT := 0.15;
ELSEIF (:CUSTOMERTYPE = 'Standard') THEN
DISCOUNT := 0.10;
ELSE
DISCOUNT := 0.05;
END IF;
IF (:ORDERAMOUNT > 1000) THEN
DISCOUNT := :DISCOUNT + 0.05;
END IF;
RETURN :DISCOUNT;
END;
$$;
SELECT
dbo.GetDiscountRate('Premium', 1200.00) as DiscountRate;
Résultat¶
DISCOUNTRATE |
|---|
0.20 |
Fonction avec boucle WHILE¶
Fonctions utilisant des boucles WHILE pour les calculs itératifs.
SQL Server¶
CREATE FUNCTION dbo.Factorial
(
@Number INT
)
RETURNS BIGINT
AS
BEGIN
DECLARE @Result BIGINT = 1
DECLARE @Counter INT = 1
WHILE @Counter <= @Number
BEGIN
SET @Result = @Result * @Counter
SET @Counter = @Counter + 1
END
RETURN @Result
END
GO
SELECT dbo.Factorial(5) as FactorialResult;
Résultat¶
FactorialResult |
|---|
120 |
Snowflake (UDF SnowScript)¶
CREATE OR REPLACE FUNCTION dbo.Factorial (NUMBER INT)
RETURNS BIGINT
LANGUAGE SQL
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "transact", "convertedOn": "10/09/2025", "domain": "no-domain-provided", "migrationid": "QsqZARsvG3aeleeXZB43fg==" }}'
AS
$$
DECLARE
RESULT BIGINT := 1;
COUNTER INT := 1;
BEGIN
WHILE (:COUNTER <= :NUMBER) LOOP
RESULT := :RESULT * :COUNTER;
COUNTER := :COUNTER + 1;
END LOOP;
RETURN :RESULT;
END;
$$;
SELECT
dbo.Factorial(5) as FactorialResult;
Résultat¶
FACTORIALRESULT |
|---|
120 |
Fonction de manipulation des chaînes¶
Opérations de chaîne complexes utilisant des boucles et une logique conditionnelle.
SQL Server¶
CREATE FUNCTION dbo.CleanPhoneNumber
(
@Phone VARCHAR(20)
)
RETURNS VARCHAR(10)
AS
BEGIN
DECLARE @Clean VARCHAR(10) = ''
DECLARE @i INT = 1
DECLARE @Char CHAR(1)
WHILE @i <= LEN(@Phone)
BEGIN
SET @Char = SUBSTRING(@Phone, @i, 1)
IF @Char BETWEEN '0' AND '9'
SET @Clean = @Clean + @Char
SET @i = @i + 1
END
RETURN @Clean
END
GO
SELECT dbo.CleanPhoneNumber('(555) 123-4567') as CleanPhone;
Résultat¶
CleanPhone |
|---|
5551234567 |
Snowflake (UDF SnowScript)¶
CREATE OR REPLACE FUNCTION dbo.CleanPhoneNumber (PHONE STRING)
RETURNS VARCHAR(10)
LANGUAGE SQL
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "transact", "convertedOn": "10/09/2025", "domain": "no-domain-provided", "migrationid": "QsqZARsvG3aeleeXZB43fg==" }}'
AS
$$
DECLARE
CLEAN VARCHAR(10) := '';
I INT := 1;
CHAR CHAR(1);
BEGIN
WHILE (:I <= LEN(:PHONE)) LOOP
CHAR := SUBSTRING(:PHONE, :I, 1);
IF (:CHAR BETWEEN '0' AND '9') THEN
CLEAN := :CLEAN + :CHAR;
END IF;
I := :I + 1;
END LOOP;
RETURN :CLEAN;
END;
$$;
SELECT
dbo.CleanPhoneNumber('(555) 123-4567') as CleanPhone;
Résultat¶
CLEANPHONE |
|---|
5551234567 |
Logique de l’instruction CASE¶
Fonctions utilisant les expressions CASE pour la catégorisation.
SQL Server¶
CREATE FUNCTION dbo.GetGrade
(
@Score INT
)
RETURNS CHAR(1)
AS
BEGIN
DECLARE @Grade CHAR(1)
SET @Grade = CASE
WHEN @Score >= 90 THEN 'A'
WHEN @Score >= 80 THEN 'B'
WHEN @Score >= 70 THEN 'C'
WHEN @Score >= 60 THEN 'D'
ELSE 'F'
END
RETURN @Grade
END
GO
SELECT dbo.GetGrade(85) as Grade;
Résultat¶
Note |
|---|
B |
Snowflake (UDF SnowScript)¶
CREATE OR REPLACE FUNCTION dbo.GetGrade (SCORE INT)
RETURNS CHAR(1)
LANGUAGE SQL
COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "transact", "convertedOn": "10/09/2025", "domain": "no-domain-provided", "migrationid": "QsqZARsvG3aeleeXZB43fg==" }}'
AS
$$
DECLARE
GRADE CHAR(1);
BEGIN
CASE
WHEN :SCORE >= 90 THEN
GRADE := 'A';
WHEN :SCORE >= 80 THEN
GRADE := 'B';
WHEN :SCORE >= 70 THEN
GRADE := 'C';
WHEN :SCORE >= 60 THEN
GRADE := 'D';
ELSE
GRADE := 'F';
END;
RETURN :GRADE;
END;
$$;
SELECT
dbo.GetGrade(85) as Grade;
Résultat¶
GRADE |
|---|
B |
Problèmes connus¶
Avertissement
Les UDFs SnowScript ne peuvent pas :
Accéder aux tables de la base de données
Utiliser des curseurs
Appeler d’autres UDFs
Contenir des fonctions d’agrégation ou de fenêtre
Exécuter des opérations DML (INSERT/UPDATE/DELETE)
Renvoyer les jeux de résultats
EWIs connexes¶
SSC-EWI-0067 : UDF a été transformée en procédure Snowflake, l’appel de procédures à l’intérieur d’une requête n’est pas pris en charge.
SSC-EWI-0068 : La fonction définie par l’utilisateur a été transformée en procédure Snowflake.
SSC-EWI-0073 : En attente de l’examen de l’équivalence fonctionnelle.