【问题标题】:Determine whether SP Parameter has a Default Value in T-SQL判断SP参数在T-SQL中是否有默认值
【发布时间】:2013-01-17 02:57:29
【问题描述】:

是否有任何方法可以从 SQL Server(我在 2012 年仅供参考)中确定 SP 的参数是否具有默认值?对此有otherthreads,但是这些建议似乎并没有准确地让我得到这些信息。

这是我尝试过的几件事;

select *
from sys.objects so join sys.parameters sp on so.object_id = sp.object_id
where so.type='P'
and so.name = 'someSp'

上面的查询返回了许多列,听起来像是我在吠叫正确的树(其中有has_default_value,default_value)但是这些似乎并没有改变我是否有一个默认值在我的 SP 中与否。 (has_default 值始终为 0,default_value 始终为 null)

exec sp_sproc_columns 'someSp'

同样的交易;上述 SP 返回的列数包括 NULLABLE 和 IS_NULLABLE;无论我的 SP 内容如何,​​NULLABLE 始终等于 1 且 IS_NULLABLE = YES。

一个注释; SQL Server 管理工作室清楚地显示与每个 SP 参数关联的元数据。

我使用 SQL Profiler 检查了在 Management Studio 的对象资源管理器中查看 SP 参数时发生的情况。当您展开参数文件夹时,将运行两个查询。第一个查询粘贴在这里有点长(尽管如果有帮助我会这样做)。它包含一个名为 DEFAULT VALUE 的列;但是据我所知,它始终为 NULL。第二个查询只是返回 SP 的主体;大概是输出到文本编辑器窗口(虽然我担心在 mgmt studio 中可能会发生一些解析!)

作为参考/只是为了确保我不会丢失我的弹珠,我创建了两个无意义的 Sps 仅用于测试。它们看起来像:

CREATE PROCEDURE TestDefaultSpValue_Default
@I          INT  = 2
AS
BEGIN
SET NOCOUNT ON;
SELECT @I
END

CREATE PROCEDURE TestDefaultSpValue_NoDefault
@I          INT
AS
BEGIN
SET NOCOUNT ON;
SELECT @I
END

【问题讨论】:

  • 按照此链接的建议尝试 sp_procedure_params_rowset - social.msdn.microsoft.com/forums/en-US/transactsql/thread/…
  • 感谢您的快速回复。恐怕没有骰子, sp_procedure_params_rowset 'TestDefaultSpValue_Default' sp_procedure_params_rowset 'TestDefaultSpValue_NoDefault' 返回相同的列和值。
  • 如链接中提到的msdn.microsoft.com/en-us/library/ms176074.aspx,SQL Server 仅在此目录视图中维护 CLR 对象的默认值;因此,对于 Transact-SQL 对象,此列的值为 0。要查看 Transact-SQL 对象中参数的默认值,请查询 sys.sql_modules 目录视图的定义列,或使用 OBJECT_DEFINITION 系统函数。
  • 我觉得除了解析DEFINITION列别无他法。

标签: sql-server tsql stored-procedures sql-server-2012


【解决方案1】:

如果参数名称有“AS”符号 - 不工作 试试我的

create procedure ViewParameters
    @procedure varchar(50)
as
    declare
        @w varchar(max),
        @p int, @p2 int,
        @t varchar(max)


    /* Andrey Rubanko 18 jul 2013 */

    /* fill temporary table with procedure body */

    select @w = definition
    from sys.sql_modules
    where object_id = object_id(@procedure)

    declare @lines table (line varchar(500), id int identity(1, 1))

    while len(@w) > 0 begin
        set @p = charindex(char(10), @w)
        if @p > 0 begin
            insert @lines(line) values(replace(replace(SUBSTRING(@w, 1, @p - 1), char(13), ''), char(9), ' '))
            set @w = SUBSTRING(@w, @p + 1, 10000)
        end else begin
            insert @lines(line) values(replace(@w, char(13), ''))
            set @w = ''
        end
    end



    /* remove comments */

    declare 
        @i int,
        @inCommentNow bit,
        @again bit

    set @i = 1
    set @inCommentNow = 0

    while @i <= (select max(id) from @lines) begin
        select @w = line from @lines where id = @i
        set @again = 0

        if @inCommentNow = 0 begin
            set @p = patindex('%--%', @w)
            if @p > 0 begin
                set @w = SUBSTRING(@w, 1, @p - 1)

                update @lines
                set line = @w
                where id = @i

            end

            set @p = patIndex('%/*%', @w)
            if @p > 0 begin
                set @p2 = PATINDEX('%*/%', @w) 
                if @p2 > 0 begin
                    update @lines
                    set line = substring(@w, 1, @p - 1) + SUBSTRING(@w, @p2 + 2, 10000)
                    where id = @i

                    set @again = 1
                end else begin
                    set @inCommentNow = 1

                    update @lines
                    set line = SUBSTRING(@w, 1, @p - 1)
                    where id = @i
                end
            end
        end

        if @inCommentNow = 1 begin
            set @p = PATINDEX('%*/%', @w)
            if @p > 0 begin
                update @lines
                set line = SUBSTRING(@w, @p + 2, 10000)
                where id = @i

                set @inCommentNow = 0
                set @again = 1
            end else 
                update @lines
                set line = ''
                where id = @i
        end

        if @again = 0
            set @i = @i + 1
    end


    /* remove all except parameters */
    declare
        @first int,
        @last int

    set @i = 1

    while @last is null begin
        select @w = line from @lines where id = @i

        if SUBSTRING(@w, 1, 2) = 'as'
            set @last = @i - 1

        set @p = PATINDEX('% as%', @w) 
        if @last is null and @p > 0  begin
            set @w = SUBSTRING(@w, 1, @p - 1)

            update @lines
            set line = @w
            where id = @i

            if charindex('@', @w) > 0
                set @last = @i
            else 
                set @last = @i - 1
        end


        set @p = CHARINDEX('@', @w)
        if @first is null and @p > 0 begin
            set @first = @i
            set @w = SUBSTRING(@w, @p, 10000)
        end

        set @i = @i + 1
    end

    delete @lines
    where @first is null 
        or id < @first
        or id > @last



    /* decode lines to paramters */

    declare @par table (ParameterName varchar(50), ParameterType varchar(50), DefaultValue varchar(50))

    declare
        @name varchar(50),
        @type varchar(50),
        @default varchar(50)

    declare c cursor for
        select line
        from @lines
    open c
    fetch next from c into @w 
    while @@FETCH_STATUS = 0 begin
        while len(@w) > 0 begin
            set @default = null

            set @w = SUBSTRING(@w, charindex('@', @w) + 1, 10000)
            set @p = CHARINDEX(',', @w)
            print 'start:' + @w
            if @p > 0 begin
                set @t = SUBSTRING(@w, 1, @p - 1)
                set @w = LTrim(RTrim(SUBSTRING(@w, @p + 1, 10000)))
            end else begin
                set @p = patindex('% as%', @w)
                if @p > 0 
                    set @t = SUBSTRING(@w, 1, @p - 1)
                else 
                    set @t = @w
                set @w = ''
            end

            print 'T=' + @t
            set @p = charindex(' ', @t) 
            if @p = 0
                print 'NameError:' + @t + ' ->' + cast(@p as varchar)
            set @name = SUBSTRING(@t, 1, @p - 1)
            set @t = SUBSTRING(@t, @p + 1, 10000)

            set @p = CHARINDEX('=', @t)
            if @p > 0 begin
                set @default = Replace(LTrim(RTrim(SUBSTRING(@t, @p + 1, 10000))), '''', '')
                set @t = SUBSTRING(@t, 1, @p - 1)
            end 

            set @p = CHARINDEX('(', @t)
            if @p > 0 
                set @type = LTrim(RTrim(SUBSTRING(@t, 1, @p - 1)))
            else
                set @type = LTrim(RTrim(@t))

            insert @par (ParameterName, ParameterType, DefaultValue)
            values(@name, @type, @default)
        end--while len(@w) > 0

        fetch next from c into @w 
    end
    close c
    deallocate c

    select *
    from @par

【讨论】:

    【解决方案2】:

    MS SQL 仅存储 CLR 存储过程和函数的默认设置,因此在这种情况下,唯一的方法是解析对象定义。 要运行该示例,您可以创建一个空白存储过程,或采用任何其他方法。

    ALTER PROCEDURE dbo.usp_test1
    (
        @a UNIQUEIDENTIFIER = NULL,
        @b DATETIME = '20100101',
        @c DATETIME = DEFAULT,
        @d BIT = 1,
        @e BIT,
        @k INT = 1,
        @f BIT = 0, @g NVARCHAR(MAX) = '23235',
        @h INT = 3,
        @j DECIMAL(10,2) = DEFAULT
    )
    WITH RECOMPILE
    AS
    BEGIN
    
        PRINT 1;
    
    END
    

    此查询返回存储过程的默认值列表:

    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.usp_test1'
                    ) 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
    

    并且通过这个查询,你可以知道存储过程中是否包含任何默认值​​​​:

    DECLARE @name SYSNAME = 'dbo.usp_test1'
    
    IF EXISTS(
        SELECT 1
        FROM (
            SELECT 
                  sm2.[object_id]
                , tokens = SUBSTRING(sm2.tokens, ISNULL(CHARINDEX('SA', sm2.tokens) + 2, 0), LEN(sm2.tokens)) 
            FROM (
                SELECT 
                      sm.[object_id]
                    , 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 = @name
            ) sm2
        ) sm3
        WHERE sm3.tokens LIKE '%=%'
    ) PRINT @name + ' have default values'
    

    【讨论】:

    • 失败,因为你可以在数据类型定义之前有一个AS。
    【解决方案3】:
    WITH t1 AS
    (
    SELECT 
       SCHEMA_NAME(SCHEMA_ID) AS [Schema]
      ,SO.name  
      , SO.type
      ,SO.Type_Desc AS [ObjectType (UDF/SP)]
      ,P.parameter_id AS [ParameterID]
      ,P.name AS [ParameterName]
      ,P.user_type_id
      /*,P.scale
      ,P.precision
      ,P.max_length
      */
      ,TYPE_NAME(P.user_type_id) AS [ParameterDataType]
     -- ,P.max_length AS [ParameterMaxBytes]
      ,CASE WHEN user_type_id = 231 THEN max_length / 2 ELSE NULL END nvarcharsize
      ,P.is_output AS [IsOutPutParameter]
      ,ltrim(rtrim(REPLACE(LEFT( OBJECT_DEFINITION (p.object_id),90000),'CREATE PROCEDURE ',''))) ProcDef
      
    FROM sys.objects AS SO
    INNER JOIN sys.parameters AS P ON SO.OBJECT_ID = P.OBJECT_ID AND SO.type = 'P'
    AND SO.Name NOT LIKE 'Report%' 
    AND SO.Name NOT LIKE '!_%' ESCAPE '!'
    )
    , T2 AS
    (
    select 
    --ProcDef 
    LEFT(ProcDef,patindex('%AS%BEGIN%',ProcDef)-2) ProcedureDef
    ,
    name Procname,
    ParameterName,
    ParameterID,
    ParameterDataType,
    nvarcharsize
    from t1 
    )
    , T3 AS
    (
    select
    substring(ProcedureDef,charindex(ParameterName,ProcedureDef) + len(parametername) + len(ParameterDataType) + 2,12) parampos
    ,
    * from t2 where procname like 'MyProcName'
    )
    , T4 AS
    (
    select
    CHARINDEX('=',parampos) haseq,
    replace(replace(replace(substring(parampos,CHARINDEX('=',parampos),100),' ',''),'=',''),',','') valdef
    ,
    * from t3
    )
    , t5 as
    (
    select
    CASE WHEN haseq > 0 THEN valdef else '' end defaultvalue,
    * from t4 
    )
    select * from t5 
    ORDER BY  Procname, parameterid
    GO
    
    
    
    
    

    【讨论】:

    • ** 您需要将 MyProcName 更改为您的 proc(s) 的名称或通配符 ** 这将获得默认值,如果您喜欢 CTE(通用表表达式),则更容易理解
    【解决方案4】:

    有更简单和正确的方法来设置SP和函数的所有输入和输出参数,请尝试以下语句。 (来源是http://www.mssqltips.com/sqlservertip/1669/generate-a-parameter-list-for-all-sql-server-stored-procedures-and-functions/

    SELECT SCHEMA_NAME(SCHEMA_ID) AS [Schema],
    SO.Name AS [ObjectName],
    SO.Type_Desc AS [ObjectType (UDF/SP)],
    PM.Parameter_ID AS [ParameterID],
    case 
    when pm.system_type_id = pm.user_type_id then 'system_type'
    else 'user_type'
    end as [TypeDescr],
    CASE
    WHEN PM.Parameter_ID = 0 THEN 'Returns'
    ELSE PM.Name
    END AS [ParameterName],
    '['+TYPE_NAME(PM.User_Type_ID)+']' AS [ParameterDataType],
    CASE 
    WHEN TYPE_NAME(PM.User_Type_ID) IN ('float', 'uniqueidentifier', 'datetime', 'bit', 'bigint', 'int', 'image', 'money', 'xml', 'varbinary', 'tinyint', 'text', 'ntext', 'smallint', 'smallmoney') THEN ''
    WHEN TYPE_NAME(PM.User_Type_ID) IN ('decimal', 'numeric') THEN '(' + CAST( Precision AS VARCHAR(4) ) + ', ' + CAST( Scale AS VARCHAR(4)) + ')'
    ELSE 
    case 
    when PM.Max_Length <> -1 then '('+CAST( PM.Max_Length AS VARCHAR(4))+')'
    when (TYPE_NAME(PM.User_Type_ID) = 'xml') or (pm.system_type_id <> pm.user_type_id) then ''
    else '(max)' 
    end
    END AS [Size],
    CASE 
    WHEN PM.Is_Output = 1 THEN 'Output'
    ELSE 'Input'
    END AS [Direction]
    FROM sys.objects AS SO
    INNER JOIN sys.parameters AS PM ON SO.OBJECT_ID = PM.OBJECT_ID
    WHERE TYPE IN ('P','FN')
    ORDER BY [Schema], SO.Name, PM.parameter_id
    

    【讨论】:

    • 你应该尝试:select * from information_schema.INFORMATION_SCHEMA.PARAMETERS WHERE specific_name = 'my_sp_name'
    猜你喜欢
    • 2014-09-02
    • 2016-04-27
    • 2019-11-08
    • 2012-01-11
    • 2012-08-27
    • 1970-01-01
    • 2012-01-05
    • 2019-02-08
    相关资源
    最近更新 更多