sql-serverssissql-server-2019sql-server-agentssis-2019

How to find value for SSIS package project parameter manually set in the SQL Server Agent job?


I have a package called MyPackage. It is part of the project MyProject.

It has three parameters:

ParameterA, ParameterB, ParameterC

I can find the values for these parameters using the catalog views object_parameters and environment_variables.

ParameterC though is more complex.

In the project configuration, ParameterC has the default value manually set to 'SomeValue'. I can find that through object_parameters as well.

In the SQL Server Agent Job running the package, the value of ParameterC is set to 'Othervalue'

And there is my question: where do I find the value 'Othervalue'?

I have written the query below but it does not give met 'Othervalue', it gives me the command executing the package. Not the value set for ParameterC which is 'Othervalue' in the SQL Server Agent Job configuration.

; WITH EnvironmentValues AS 
(
    SELECT 
        er.project_id,
        ev.name AS variable_name,
        CAST(ev.value AS nvarchar(max)) AS environment_value
    FROM 
        SSISDB.catalog.environment_references er
    LEFT JOIN 
        SSISDB.catalog.environment_references er_ref ON er.reference_id = er_ref.reference_id
    LEFT JOIN 
        SSISDB.catalog.environment_variables ev ON er_ref.environment_folder_name = ev.name
),
DesignAndDefaultValues AS 
(
    SELECT 
        op.project_id,
        op.object_name AS package_name,
        op.parameter_name,
        CAST(op.design_default_value AS nvarchar(max)) design_default_value,
        CAST(op.default_value AS nvarchar(max)) default_value,
        op.object_type
    FROM
        SSISDB.catalog.object_parameters op
    LEFT JOIN 
        SSISDB.catalog.projects p ON op.project_id = p.project_id
    WHERE 
        op.parameter_name NOT LIKE 'CM.%' 
        --  AND op.object_type = 20
        AND p.project_id = 27
),
JobStepParameters AS 
(
    -- Assuming that you have a way to map job step parameters manually set in the SQL Agent job
    SELECT 
        js.job_id,
        js.step_id,
        j.name AS job_name,
        j.enabled as job_enabled,
        js.command AS package_command,
        -- Extract the relevant SSIS package and parameter values from the job step command
        -- This part is tricky, as it depends on how the parameters are passed to the SSIS package
        -- Adjust the extraction logic as needed
        -- Example placeholder:
        js.command AS manually_set_value
    FROM
        msdb.dbo.sysjobs j
    LEFT JOIN 
        msdb.dbo.sysjobsteps js ON j.job_id = js.job_id
    WHERE
        js.subsystem = 'SSIS'
)
INSERT INTO @Resultstable
    SELECT DISTINCT
        f.name AS SSIS_Folder,
        p.name AS SSIS_Project,
        d.package_name AS SSIS_Package_Name,
        d.parameter_name AS SSIS_Parameter_Name,
        jp.job_name AS SQL_Agent_Job_Name,
        jp.job_enabled,
        COALESCE(jp.manually_set_value, e.environment_value,
                 d.default_value, d.design_default_value) AS Parameter_Value_Used,
        CASE
            WHEN jp.manually_set_value IS NOT NULL 
                THEN 'Job manually set'
            WHEN e.environment_value IS NOT NULL 
                THEN 'Environment variable'
            WHEN d.default_value IS NOT NULL 
                THEN 'Default value in package'
            ELSE 'Design default value'
        END AS Value_Source
    FROM 
        SSISDB.catalog.folders f
    LEFT JOIN 
        SSISDB.catalog.projects p ON f.folder_id = p.folder_id
    LEFT JOIN 
        DesignAndDefaultValues d ON p.project_id = d.project_id
    LEFT JOIN 
        EnvironmentValues e ON p.project_id = e.project_id
                            AND d.parameter_name = e.variable_name
    LEFT JOIN 
        JobStepParameters jp ON jp.package_command LIKE '%' + d.package_name + '%'
    ORDER BY
        1, 2, 3, 4, 5

Edit: these values can be found in the js.command mentioned here above.

It looks like this:

/ISSERVER "\"\SSISDB\MyFolder\MyPackage.dtsx\"" /SERVER "\"MyServer\"" /ENVREFERENCE 13 /Par "\"$Project::ParameterC\"";"\"OtherValue\"" /Par "\"$Project::ParameterD\"";"\"ValueforParameterD\"" /Par "\"$ServerOption::LOGGING_LEVEL(Int16)\"";2 /Par "\"$ServerOption::SYNCHRONIZED(Boolean)\"";True /CALLERINFO SQLAGENT /REPORTING E

I need to extract the part between the first /Par "\"$Project:: and /CALLERINFO.

Then I need to decompose it as follows:

/Par "\"$Project::ParameterC\"";"\"OtherValue\"" 
/Par "\"$Project::ParameterD\"";"\"ValueforParameterD\"" 
/Par "\"$ServerOption::LOGGING_LEVEL(Int16)\"";2 
/Par "\"$ServerOption::SYNCHRONIZED(Boolean)\"";True

Keep only the pars that contain the text '/Par "\"$Project:::':

/Par "\"$Project::ParameterC\"";"\"OtherValue\"" 
/Par "\"$Project::ParameterD\"";"\"ValueforParameterD\"" 

Decompose these further so that you have these results as a recordset

parameter  | value
-----------+--------------------
ParameterC | OtherValue
ParameterD | ValueforParameterD

How do I extract the parameters and their values from the js.command?


Solution

  • I needed two functions to get this working:

    CREATE OR ALTER function [ssis].[split_command_text]
    (
    @command NVARCHAR(MAX)
    )
    
    RETURNS @Resultstable TABLE
    (   [command] nvarchar(max),
        parameter_name nvarchar(100),
       [value]  nvarchar(max)
    )
    
    AS
    BEGIN
    IF @command like '%/Par "\"$Project::%'
        BEGIN
        ;WITH NumberedParams AS (
            SELECT 
                ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn,
                value AS param_pair
            FROM STRING_SPLIT(REPLACE(@command, '/Par ', '|'), '|')
            WHERE value LIKE '%$Project::%'
        ),
        ExtractedParams AS (
            SELECT 
                rn,
                SUBSTRING(param_pair, 
                    CHARINDEX('$Project::', param_pair) + 10,
                    CHARINDEX('""', param_pair, CHARINDEX('$Project::', param_pair) + 10) - (CHARINDEX('$Project::', param_pair) + 10)
                ) AS parameter,
                CASE
                    WHEN CHARINDEX('";', param_pair) > 0 THEN
                        SUBSTRING(param_pair, 
                            CHARINDEX('";', param_pair) + 2,
                            CASE 
                                WHEN CHARINDEX(' /', param_pair, CHARINDEX('";', param_pair)) > 0 
                                THEN CHARINDEX(' /', param_pair, CHARINDEX('";', param_pair)) - (CHARINDEX('";', param_pair) + 2)
                                ELSE LEN(param_pair) - (CHARINDEX('";', param_pair) + 1)
                            END
                        )
                    ELSE NULL
                END AS value
            FROM NumberedParams
        )
        insert into @Resultstable
        SELECT 
            @command as command,
            REPLACE(parameter, '\', '') AS parameter,
            COALESCE(NULLIF(REPLACE(REPLACE(REPLACE(value, '"', ''), '\', ''), ';', ''), ''), 'NULL') AS value
        FROM ExtractedParams
        WHERE parameter NOT LIKE '%ServerOption%'
        ORDER BY rn;
        END
    RETURN
    END;
    

    and

    create or alter function ssis.get_actual_used_values(
    @SSIS_Folder as nvarchar(255) = NULL,
    @SSIS_Project as nvarchar(255) = NULL,
    @SSIS_Parameter_Name as nvarchar(255) = NULL,
    @Environment_name as nvarchar(255) = NULL,
    @SQL_Agent_Job_Name as nvarchar(255) = NULL
    ) 
    
    RETURNS TABLE AS  RETURN
    
    (
    WITH EnvironmentValues AS (
        SELECT er.project_id,
            ev.name AS variable_name,
            cast(ev.value as nvarchar(max)) AS environment_value,
            e.name as environment_name,
            e.environment_id,
            er.reference_id,
            er.environment_folder_name
        FROM SSISDB.catalog.environment_variables ev 
        LEFT JOIN SSISDB.catalog.environments e on ev.environment_id = e.environment_id
        LEFT JOIN  SSISDB.catalog.environment_references er on er.environment_name = e.name
        LEFT JOIN SSISDB.catalog.projects pr on er.project_id = pr.project_id
        LEFT JOIN SSISDB.catalog.folders f1 on e.folder_id = f1.folder_id
        LEFT JOIN SSISDB.catalog.folders f2 on pr.folder_id = f2.folder_id
    
        WHERE 1=1
        AND  f1.name = f2.name
        AND (@Environment_name is null or e.name = @Environment_name)
        AND (@SSIS_Project is null or pr.name = @SSIS_Project)
    ),
    DesignAndDefaultValues AS (
        SELECT distinct op.project_id, 
            p.name collate SQL_Latin1_General_CP1_CI_AS AS SSIS_project_name,
            op.parameter_name collate SQL_Latin1_General_CP1_CI_AS as parameter_name,
            cast(op.design_default_value as nvarchar(max)) collate SQL_Latin1_General_CP1_CI_AS as design_default_value,
            cast(op.default_value as nvarchar(max)) collate SQL_Latin1_General_CP1_CI_AS as default_value
            FROM SSISDB.catalog.object_parameters op
            LEFT JOIN SSISDB.catalog.projects p ON op.project_id = p.project_id
        WHERE op.parameter_name collate SQL_Latin1_General_CP1_CI_AS not like 'CM.%' and op.object_type = 20 --<-- 20 = project[parameter, 30 = package parameter
        AND (@SSIS_Parameter_Name is null or op.parameter_name = @SSIS_Parameter_Name)
        AND (@SSIS_Project is null or p.name = @SSIS_Project)
        --and op.project_id = 231
        --order by 2,3,4,5
    ),
    JobStepParameters AS (
        -- Assuming that you have a way to map job step parameters manually set in the SQL Agent job
        SELECT 
        js.command as package_command
        ,dp.parameter_name
        ,dp.[value] as manually_set_value
        FROM msdb.dbo.sysjobs j
        LEFT JOIN msdb.dbo.sysjobsteps js ON j.job_id = js.job_id
        OUTER APPLY ssis.split_command_text(js.command) as dp
        WHERE js.subsystem = 'SSIS' and js.command = dp.command
        AND (@SSIS_Parameter_Name is null or dp.parameter_name = @SSIS_Parameter_Name)
        AND (@SQL_Agent_Job_Name is null or j.name = @SQL_Agent_Job_Name)
        --order by 1,2,3
    )
    SELECT distinct f.name AS SSIS_Folder,
        p.name AS SSIS_Project,
        d.parameter_name AS SSIS_Parameter_Name,
        COALESCE(
            jp.manually_set_value,
            d.default_value,
            env_val.environment_value,
            d.design_default_value
        ) AS Actual_Parameter_Value_Used,
        CASE
            WHEN jp.manually_set_value IS NOT NULL THEN 'Value manually set in SQL Agent Job'
            WHEN d.default_value  IS NOT NULL THEN 'Value manually set in project configuration'
            WHEN env_val.environment_value IS NOT NULL THEN 'Takes value from environment' 
            WHEN d.design_default_value  IS NOT NULL THEN  'Parameter not configured so value from package'
            ELSE '<Cannot be determined>'
        END AS why_this_value,
        j.name AS SQL_Agent_Job_Name,
        j.enabled as SQL_Agent_enabled,
    --  d.package_name,
        isnull(env_val.environment_folder_name,f.name) + '\' + env_val.environment_name as related_environment,
        js.command
        --, isnull(env_val.environment_folder_name, f.name ) as env_name
        --,p.name as pname
        --,cast(env_val.reference_id as varchar(10)) as envrefer
    
    
    FROM SSISDB.catalog.folders f
        LEFT JOIN SSISDB.catalog.projects p ON f.folder_id = p.folder_id
        LEFT JOIN DesignAndDefaultValues d ON p.project_id = d.project_id
        LEFT JOIN EnvironmentValues env_val ON p.project_id = env_val.project_id and  d.parameter_name = env_val.variable_name
        LEFT JOIN msdb.dbo.sysjobsteps js   ON  1=1
                                            --AND js.command  collate SQL_Latin1_General_CP1_CI_AS LIKE '%' + isnull(env_val.environment_folder_name, f.name )   + '%' collate SQL_Latin1_General_CP1_CI_AS 
                                            AND js.command  collate SQL_Latin1_General_CP1_CI_AS LIKE '%' + p.name + '%' collate SQL_Latin1_General_CP1_CI_AS 
                                            AND js.command  collate SQL_Latin1_General_CP1_CI_AS LIKE '%/ENVREFERENCE ' + cast(env_val.reference_id as varchar(10))+ '%' collate SQL_Latin1_General_CP1_CI_AS
                                            
        LEFT JOIN msdb.dbo.sysjobs j ON j.job_id = js.job_id
        LEFT JOIN JobStepParameters jp ON   jp.package_command collate SQL_Latin1_General_CP1_CI_AS = js.command collate SQL_Latin1_General_CP1_CI_AS  
                                            and d.parameter_name = jp.parameter_name
                                            and env_val.variable_name collate SQL_Latin1_General_CP1_CI_AS = jp.parameter_name collate SQL_Latin1_General_CP1_CI_AS
    WHERE 1=1
    and (@SSIS_Folder is null or f.name = @SSIS_Folder)
    and (@Environment_name is null or env_val.environment_name = @Environment_name)
    and (@SSIS_Parameter_Name is null or d.parameter_name = @SSIS_Parameter_Name)
    and (@SSIS_Project is null or p.name = @SSIS_Project)
    and (@SQL_Agent_Job_Name is null or j.name = @SQL_Agent_Job_Name)
        
    );
    

    Calling

    select * from ssis.get_actual_used_values(NULL,NULL,NULL,NULL,NULL) order by 1,2,3,4,5,6,7,8,9
    

    will tell you everything you'll want to know about your project-parameters. And yes, I know the code is ugly, be my guest to tidy it up and improve on it :)