Échec de la création de la variable d'environnement SSIS 2012

12

Je travaille un script pour porter un environnement d'un serveur à un autre. Je rencontre un problème d'appel catalog.create_environment_variabledans lequel j'obtiens l'erreur "Le type de données de la valeur d'entrée n'est pas compatible avec le type de données de la" chaîne "." sortant du proc "check_data_type_value".

Ce qui est étrange, c'est que si je laisse le script GUI sortir les variables, cette requête fonctionnera

DECLARE @var sql_variant = N'\\myserver\ssisdata'
EXEC [catalog].[create_environment_variable]
    @variable_name = N'FolderBase'
,   @sensitive = False
,   @description = N''
,   @environment_name = N'Development'
,   @folder_name = N'POC'
,   @value = @var
,   @data_type = N'String'
GO

Cependant, adopter cette approche de script ne fonctionne pas. Le travail que j'ai fait indique que ce message d'erreur est généralement résolu en utilisant le type de données nvarchar au lieu de varchar. Cependant, ce n'est pas le cas pour mes affaires.

Ligne 108 pour le script suivant. Mon hypothèse est que c'est quelque chose de loufoque avec la sql_variant mais je n'ai aucune idée de ce qu'est cette chose.

USE SSISDB;
GO

DECLARE
    @folder_id bigint
,   @folder_name nvarchar(128) = N'POC'
,   @environment_name nvarchar(128) = N'Development'
,   @environment_description nvarchar(1024)
,   @reference_id bigint
,   @variable_name nvarchar(128)
,   @data_type nvarchar(128)
,   @sensitive bit
,   @value sql_variant
,   @description nvarchar(1024);

IF NOT EXISTS
(
    SELECT * FROM catalog.folders AS F WHERE F.name = @folder_name
)
BEGIN
    EXECUTE catalog.create_folder
        @folder_name = @folder_name
    ,   @folder_id = @folder_id OUTPUT;

    PRINT CONCAT('Folder "', @folder_name, '" has been created with a folder_id of ', @folder_id)
END

IF NOT EXISTS
(
    SELECT * FROM catalog.environments AS E WHERE E.name = @environment_name 
    AND E.folder_id = (SELECT F.folder_id FROM catalog.folders AS F WHERE F.name = @folder_name)
)
BEGIN
    PRINT CONCAT('Creating environment ',  @environment_name);

    EXECUTE catalog.create_environment
        @folder_name = @folder_name
    ,   @environment_name = @environment_name
    ,   @environment_description = @environment_description;
END

DECLARE
    @EnvironmentVariables TABLE
(
    folder_name nvarchar(128)
,   environment_name nvarchar(128)
,   variable_name nvarchar(128)
,   description nvarchar(1024)
,   data_type nvarchar(128)
,   sensitive bit
,   value sql_variant
);

INSERT INTO
    @EnvironmentVariables
SELECT
    E.folder_name
,   E.environment_name
,   S.name
,   S.description
,   S.type
,   S.sensitive
,   S.value
FROM
(
    SELECT 'FolderBase','Root for ssis processing','String',CAST(0 AS bit),'\\myserver\ssisdata'
    UNION ALL SELECT 'AuditConnectionString','Conn to audit db','String',CAST(0 AS bit),'Data Source=SQLETL01;Initial Catalog=Audit;Provider=SQLNCLI11.1;Integrated Security=SSPI;Auto Translate=False;'
) AS S (name, description, type, sensitive, value)
CROSS APPLY
(
    SELECT
        E.name AS environment_name
    ,   F.name AS folder_name
    FROM
        catalog.folders AS F
        INNER JOIN
            catalog.environments AS E
            ON E.folder_id = F.folder_id
    WHERE
        F.name = @folder_name
        AND E.name = @environment_name
) E;


DECLARE Csr CURSOR FORWARD_ONLY STATIC FOR
SELECT
    EV.variable_name
,   EV.description
,   EV.data_type
,   EV.sensitive
,   EV.value
FROM
    @Environmentvariables AS EV;

OPEN Csr;
FETCH NEXT FROM Csr INTO
    @variable_name
,   @description
,   @data_type
,   @sensitive
,   @value;

WHILE @@FETCH_STATUS = 0
BEGIN

    BEGIN TRY
            -- THERE BE MONSTERS AHEAD
        -- The data type of the input value is not compatible with the data type of the 'String'. 
        EXECUTE catalog.create_environment_variable
            @variable_name = @variable_name
        ,   @sensitive = @sensitive
        ,   @description = @description
        ,   @environment_name = @environment_name
        ,   @folder_name = @folder_name
        ,   @value = @value
        ,   @data_type = @data_type
    END TRY
    BEGIN CATCH
        SELECT 
            @folder_name        AS folder_name
        ,   @environment_name   AS environment_name
        ,   @variable_name      AS variable_name
        ,   @data_type          AS data_type
        ,   @sensitive          AS sensitive
        ,   @value              AS value
        ,   @description        AS description
        ,   ERROR_NUMBER()AS error_number --returns the number of the error.
        ,   ERROR_SEVERITY() AS error_severity --returns the severity.
        ,   ERROR_STATE()AS error_state  --returns the error state number.
        ,   ERROR_PROCEDURE() AS error_procedure --returns the name of the stored procedure or trigger where the error occurred.
        ,   ERROR_LINE() AS error_line --returns the line number inside the routine that caused the error.
        ,   ERROR_MESSAGE() AS error_message; --returns the complete text of the error message. The text includes the values supplied for any substitutable parameters, such as lengths, object names, or times.

    END CATCH  

    FETCH NEXT FROM Csr INTO
        @variable_name
    ,   @description
    ,   @data_type
    ,   @sensitive
    ,   @value;
END

CLOSE Csr;
DEALLOCATE Csr;
billinkc
la source
7
Fils. Je suis déçu.
swasheck
5
@swasheck Savez-vous combien de fierté a été avalée avant de pouvoir cliquer sur le bouton Soumettre?
billinkc
Je ne suis pas déçu, tu viens de sauver ma nuit. Freaking nVarChar
RThomas

Réponses:

10

"Le travail que j'ai fait indique que ce message d'erreur est généralement résolu en utilisant le type de données nvarchar au lieu de varchar. Cependant, ce n'est pas le cas pour mes affaires." Ou est- ce le cas?

J'ai apporté deux modifications à mon curseur. Le premier est dans mon bloc CATCH. J'ai relu l'article sur le type de données sql_variant et j'ai suivi avec sql_variant_property . J'ai ajouté un appel à cela dans mon bloc catch, en m'attendant à voir nvarcharmais voilà, il rapporte varcharcomme mon BaseType.

Sachant cela et que toutes mes données sources sont basées sur des caractères, j'ai triché et ajouté la @localvariable avec un cast explicite à nvarchar et cela fonctionne comme par magie.

WHILE @@FETCH_STATUS = 0
BEGIN

    BEGIN TRY
        -- THERE BE MONSTERS AHEAD
        -- The data type of the input value is not compatible with the data type of the 'String'. 
        DECLARE
            @local nvarchar(4000) = CONVERT(nvarchar(4000), @value);
        EXECUTE catalog.create_environment_variable
            @variable_name = @variable_name
        ,   @sensitive = @sensitive
        ,   @description = @description
        ,   @environment_name = @environment_name
        ,   @folder_name = @folder_name
        ,   @value = @local
        ,   @data_type = @data_type
    END TRY
    BEGIN CATCH
        SELECT 
            @folder_name        AS folder_name
        ,   @environment_name   AS environment_name
        ,   @variable_name      AS variable_name
        ,   @data_type          AS data_type
        ,   @sensitive          AS sensitive
        ,   @value              AS value
        ,   SQL_VARIANT_PROPERTY(@value, 'BaseType') As BaseType
        ,   @description        AS description
        ,   ERROR_NUMBER()AS error_number --returns the number of the error.
        ,   ERROR_SEVERITY() AS error_severity --returns the severity.
        ,   ERROR_STATE()AS error_state  --returns the error state number.
        ,   ERROR_PROCEDURE() AS error_procedure --returns the name of the stored procedure or trigger where the error occurred.
        ,   ERROR_LINE() AS error_line --returns the line number inside the routine that caused the error.
        ,   ERROR_MESSAGE() AS error_message; --returns the complete text of the error message. The text includes the values supplied for any substitutable parameters, such as lengths, object names, or times.

    END CATCH  

    FETCH NEXT FROM Csr INTO
        @variable_name
    ,   @description
    ,   @data_type
    ,   @sensitive
    ,   @value;
END

CLOSE Csr;
DEALLOCATE Csr;

Analyse de la cause originelle

Alors que je commençais à rédiger un résumé des résultats, j'ai découvert la déconnexion. Pendant que je chargeais ma table temporaire, @EnvironmentVariables, je l'avais initialement fournie directement catalog.environment_variables.pour la rendre plus portable, j'ai copié les valeurs sous forme d'instructions SELECT. C'est là que j'ai foiré. Lorsque j'ai reconstitué ces valeurs, j'ai transformé les chaînes Unicode en 'chaînes mercan. Ils ont été écrits dans la colonne de type sql_variant en tant que non-unicode qui a ensuite explosé quand il a été passé dans le proc pour validation. Si je fais correctement précéder mes chaînes du Nmodificateur (?), Elles sont stockées sous nvarchar.

FROM
(
    SELECT 'FolderBase','Root for ssis processing','String',CAST(0 AS bit),N'\\myserver\ssisdata'
    UNION ALL SELECT 'AuditConnectionString','Conn to audit db','String',CAST(0 AS bit),N'Data Source=SQLETL01;Initial Catalog=Audit;Provider=SQLNCLI11.1;Integrated Security=SSPI;Auto Translate=False;'
) AS S (name, description, type, sensitive, value)
billinkc
la source
J'ai utilisé une variante de ce thefirstsql.com/2013/05/28/… pour générer mon script /. Si je comprends bien, je dois m'assurer que tous mes littéraux de chaîne commencent par Nmais j'ai toujours ce problème. En fait, je reçois des plaintes concernant booléen et datetime mais aucun de mes paramètres n'utilise ces types de données. Avez-vous une idée?
Nick.McDermaid
1

Juste pour ajouter à @billinkc une excellente réponse (vous saviez sûrement que vous seriez le seul à répondre à cette question !!) et enrichir les connaissances autour de cela ....

Voici un exemple de code, généré automatiquement à partir d'ici https://thefirstsql.com/2013/05/28/ssis-2012-easily-copy-environment-variables-to-new-servers-or-new-environments/ qui génère l'erreur :

DECLARE @var sql_variant

SET @var = '2'
IF NOT EXISTS (
    SELECT 1 FROM [catalog].[environment_variables] 
    WHERE environment_id = @environment_id 
    AND name = N'MyVariable')
EXEC [catalog].[create_environment_variable] 
    @variable_name=N'SystemCd', 
    @sensitive=0, @description=N'', 
    @environment_name=@env_name, 
    @folder_name=@folder, 
    @value=@var, 
    @data_type=N'Int64'

Plus précisément, l'erreur est

Msg 27147, niveau 16, état 1, procédure internal.check_data_type_value, ligne 22 [ligne de démarrage par lots 0] Le type de données de la valeur d'entrée n'est pas compatible avec le type de données de 'Int64'.

J'ai également eu d'autres erreurs pour datetime et booléen

Donc, sans aller trop loin dans la compréhension du problème, la solution était essentiellement d'utiliser un type de données spécifique plutôt que de sql_variantpasser une valeur dans le @valueparamètre.

Pour Int64 et Boolean, vous pouvez simplement coder en dur la valeur, mais pour datetimevous devez pré-déclarer une variable de type datetimeet l'utiliser - vous ne pouvez pas simplement passer un littéral de date de chaîne

C'est la première fois que je vois un paramètre dans une procédure stockée accepter apparemment une variété de types de données.

DECLARE @var sql_variant
DECLARE @var_int int

SET @var = '2'
IF NOT EXISTS (
    SELECT 1 FROM [catalog].[environment_variables] 
    WHERE environment_id = @environment_id 
    AND name = N'MyVariable')
EXEC [catalog].[create_environment_variable] 
    @variable_name=N'SystemCd', 
    @sensitive=0, @description=N'', 
    @environment_name=@env_name, 
    @folder_name=@folder, 
    @value=@var_int, 
    @data_type=N'Int64'
Nick.McDermaid
la source
Il est très logique d'être explicite au lieu d'utiliser le format unique de SQL-Server-2000
billinkc