【问题标题】:Get default value of stored procedure parameter获取存储过程参数的默认值
【发布时间】:2018-05-09 03:37:41
【问题描述】:

我在下面创建了具有默认值的存储过程:

CREATE PROCEDURE [dbo].[Sample1] 
     @OrderID INT = 10285
AS
    SELECT ProductName, OrderID
    FROM Products P, [Order Details] Od
    WHERE Od.ProductID = P.ProductID 
      AND Od.OrderID = @OrderID

尝试使用sys.parameters获取参数的默认值(10285)。

Select a.object_id, a.default_value
from sys.parameters a
inner join sys.types b on b.system_type_id = a.system_type_id
where Object_id = object_id('[dbo].[Sample1]')

但我得到 NULL 为 default_value,而我期待 10285default_value

有没有办法获取默认值?

【问题讨论】:

标签: sql-server stored-procedures parameters default-value ssms-2012


【解决方案1】:

看起来Microsoft has neglected this topic 并且没有简单的方法可以找到参数默认值,即使特定参数上存在或不存在默认值:

众所周知,T-SQL 存储过程的参数默认值是不 存储在 sys.parameters、all_parameters 和 system_parameters 中。他们 也不会通过 sp_sproc_columns、sys.columns 或 sp_procedure_params_rowset。

来自微软的反馈:

正如 Tibor Karaszi 所发布的,BOL 文档指出“仅限 SQL Server 在此目录视图中维护 CLR 对象的默认值; 因此,对于 Transact-SQL 对象,此列的值为 0。到 查看 Transact-SQL 对象中参数的默认值,查询 sys.sql_modules 目录视图的定义列,或使用 OBJECT_DEFINITION 系统函数。”

我们甚至不存储指示参数默认的位 育空地区的价值。

我已经测试了第一个代码 sn-p in this answer,它似乎适用于您的简单示例:

SELECT  
      data3.name
    , [default_value] = REVERSE(RTRIM(SUBSTRING(
          data3.rtoken
        , CASE 
            WHEN CHARINDEX(N',', data3.rtoken) > 0 
                THEN CHARINDEX(N',', data3.rtoken) + 1
            WHEN CHARINDEX(N')', data3.rtoken) > 0 
                THEN CHARINDEX(N')', data3.rtoken) + 1
            ELSE 1 
          END
        , LEN(data3.rtoken)
      )))
FROM (
    SELECT  
          data2.name
        , rtoken = REVERSE(
            SUBSTRING(ptoken
                    , CHARINDEX('=', ptoken, 1) + 1
                    , LEN(data2.ptoken))
                )
    FROM (
        SELECT  
              data.name
            , ptoken = SUBSTRING(
                  data.tokens
                , token_pos + name_length + 1
                , ISNULL(ABS(next_token_pos - token_pos - name_length - 1), LEN(data.tokens))
            )
        FROM (
            SELECT  
                  sm3.tokens
                , p.name
                , name_length = LEN(p.name)
                , token_pos = CHARINDEX(p.name, sm3.tokens)
                , next_token_pos = CHARINDEX(p2.name, sm3.tokens)
            FROM (
                SELECT 
                      sm2.[object_id]
                    , sm2.[type]
                    , tokens = REVERSE(SUBSTRING(sm2.tokens, ISNULL(CHARINDEX('SA', sm2.tokens) + 2, 0), LEN(sm2.tokens))) 
                FROM (
                    SELECT 
                          sm.[object_id]
                        , o.[type]
                        , tokens = REVERSE(SUBSTRING(
                                      sm.[definition]
                                    , CHARINDEX(o.name, sm.[definition]) + LEN(o.name) + 1
                                    , ABS(CHARINDEX(N'AS', sm.[definition]))
                                 )  
                        ) 
                    FROM sys.sql_modules sm WITH (NOLOCK)
                    JOIN sys.objects o WITH (NOLOCK) ON sm.[object_id] = o.[object_id]
                    JOIN sys.schemas s WITH (NOLOCK) ON o.[schema_id] = s.[schema_id] 
                    WHERE o.[type] = 'P '
                        AND s.name + '.' + o.name = 'dbo.Sample1'
                ) sm2
                WHERE sm2.tokens LIKE '%=%'
            ) sm3
            JOIN sys.parameters p WITH (NOLOCK) ON sm3.[object_id] = p.[object_id]
            OUTER APPLY (
                SELECT p2.name
                FROM sys.parameters p2 WITH (NOLOCK) 
                WHERE p2.is_output = 0
                    AND sm3.[object_id] = p2.[object_id] 
                    AND p.parameter_id + 1 = p2.parameter_id
            ) p2
            WHERE p.is_output = 0
        ) data
    ) data2
    WHERE data2.ptoken LIKE '%=%'
) data3

但是,对于一个期望可以从系统视图中轻松查询的任务来说,这真的很难看。

【讨论】:

    【解决方案2】:

    我同意默认存储过程参数值应通过 SQL Server 目录视图公开。

    T-SQL 解析方法可能在很多情况下都有效,但很脆弱。考虑使用 TransactSQL ScriptDOM。下面是一个混合使用 PowerShell 和 C# 的示例。并不是说这对所有情况都是完美的,但它似乎可以处理我迄今为止向它抛出的所有参数。

    在此示例中,我使用了我的 SSMS 安装中的 Microsoft.SqlServer.TransactSql.ScriptDom.dll 程序集,但它可以从 NuGet Gallery 下载。

    try
    {
        Add-type -LiteralPath @("C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\ManagementStudio\Extensions\Application\Microsoft.SqlServer.TransactSql.ScriptDom.dll");
        Add-type `
            -ReferencedAssemblies @("C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\ManagementStudio\Extensions\Application\Microsoft.SqlServer.TransactSql.ScriptDom.dll") `
            -TypeDefinition @"
    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.SqlServer.TransactSql.ScriptDom;
    using System.IO;
    public static class ProcParser
    {
    
        public static List<StoredProcedureParameter> GetStoredProcedureParameters(string storedProcedureDefinition)
        {
    
            StringReader reader = new StringReader(storedProcedureDefinition);
            var parser = new TSql140Parser(true);
    
            IList<ParseError> errors;
            TSqlFragment sqlFragment = parser.Parse(reader, out errors);
    
            if (errors.Count > 0)
            {
                throw new Exception(`"Error parsing stored procedure definition`");
            }
    
            SQLVisitor sqlVisitor = new SQLVisitor();
            sqlFragment.Accept(sqlVisitor);
    
            return sqlVisitor.StoredProcedureParameters;
    
        }
    
    }
    
    internal class SQLVisitor : TSqlFragmentVisitor
    {
    
        public List<StoredProcedureParameter> StoredProcedureParameters = new List<StoredProcedureParameter>();
    
        public override void ExplicitVisit(ProcedureParameter node)
        {
    
            var p = StoredProcedureParameter.CreateProcedureParameter(node);
            StoredProcedureParameters.Add(p);
    
        }
    
    }
    
    public class StoredProcedureParameter
    {
        public string ParameterName;
        public string ParameterType;
        public string ParameterDirection = null;
        public string DefaultParameterValue = null;
    
        public static StoredProcedureParameter CreateProcedureParameter(ProcedureParameter node)
        {
            var param = new StoredProcedureParameter();
    
            //parameter name
            param.ParameterName = node.VariableName.Value;
    
            //data type
            switch (((ParameterizedDataTypeReference)node.DataType).Parameters.Count)
            {
                case 0:
                    if (node.DataType.Name.Identifiers.Count == 1)
                    {
                        param.ParameterType = node.DataType.Name.Identifiers[0].Value;
                    }
                    else
                    {
                        //schema-qualified type name
                        param.ParameterType = node.DataType.Name.Identifiers[0].Value + `".`" + node.DataType.Name.Identifiers[1].Value;
                    }
                    break;
                case 1:
                    param.ParameterType = node.DataType.Name.Identifiers[0].Value + "(" + ((ParameterizedDataTypeReference)node.DataType).Parameters[0].Value + ")";
                    break;
                case 2:
                    param.ParameterType = node.DataType.Name.Identifiers[0].Value + "(" + ((ParameterizedDataTypeReference)node.DataType).Parameters[0].Value + "," + ((ParameterizedDataTypeReference)node.DataType).Parameters[1].Value + ")";
                    break;
            }
    
            //default value
            if (node.Value != null)
            {
                param.DefaultParameterValue = node.ScriptTokenStream[node.LastTokenIndex].Text;
            }
    
            //direction
            if (node.Modifier == ParameterModifier.Output)
            {
                param.ParameterDirection = `"OUTPUT`";
            }
            else if (node.Modifier == ParameterModifier.ReadOnly)
            {
                param.ParameterDirection = `"READONLY`";
            }
            else
            {
                param.ParameterDirection = `"INPUT`";
            }
    
            return param;
    
        }
    
        public override string ToString()
        {
    
            var sb = new StringBuilder();
            sb.Append(ParameterName);
            sb.Append(`" `");
            sb.Append(ParameterType);
            if (DefaultParameterValue != null)
            {
                sb.Append(`" `");
                sb.Append(DefaultParameterValue);
            }
            sb.Append(`" `");
            sb.Append(ParameterDirection);
            return sb.ToString();
    
        }
    
    }
    "@
    
    }
    catch [System.Reflection.ReflectionTypeLoadException]
    {
      Write-Host "Message: $($_.Exception.Message)"
      Write-Host "StackTrace: $($_.Exception.StackTrace)"
      Write-Host "LoaderExceptions: $($_.Exception.LoaderExceptions)"
      throw;
    }
    
    Function Get-ProcText($connectionString, $procName)
    {
        $connection = New-Object System.Data.SqlClient.SqlConnection($connectionString);
        $connection.Open();
        $command = New-Object System.Data.SqlClient.SqlCommand("SELECT definition FROM sys.sql_modules WHERE object_id = OBJECT_ID(@ProcName);", $connection);
        $procNameParameter = $command.Parameters.Add((New-Object System.Data.SqlClient.SqlParameter("@ProcName", [System.Data.SqlDbType]::NVarChar, 261)));
        $procNameParameter.Value = $procName;
        $procText = $command.ExecuteScalar();
        $connection.Close();
        return $procText;
    }
    
    ############
    ### main ###
    ############
    try {
    
        # get proc text definition from database
        $procText = Get-ProcText `
            -connectionString "Data Source=.;Initial Catalog=tempdb;Integrated Security=SSPI" `
            -procName "dbo.testproc";
    
        # parse parameters from proc text
        $procParameters = [ProcParser]::GetStoredProcedureParameters($procText);
    
        # display parameter values
        foreach($procParameter in $procParameters)
        {
            Write-Host "ParameterName=$($procParameter.ParameterName)";
            Write-Host "`tParameterType=$($procParameter.ParameterType)";
            Write-Host "`tDefaultParameterValue=$($procParameter.DefaultParameterValue)";
            Write-Host "`tParameterDirection=$($procParameter.ParameterDirection)";
        }
    
    }
    catch {
        throw;
    }
    

    【讨论】:

      猜你喜欢
      • 2012-08-06
      • 2016-11-04
      • 1970-01-01
      • 1970-01-01
      • 2010-09-14
      • 2011-11-02
      • 1970-01-01
      • 2016-08-31
      相关资源
      最近更新 更多