如何确定SQL Server存储过程参数是否具有默认值?

时间:2009-11-04 19:24:43

标签: .net sql-server stored-procedures

有没有办法以编程方式确定SQL Server存储过程参数是否具有默认值? (如果您可以确定默认值是什么,则可以获得奖励。)SqlCommandBuilder.DeriveParameters()甚至没有尝试。

提前感谢您的帮助!

编辑:老实说,我不关心它是SQL查询,SMO对象等。

7 个答案:

答案 0 :(得分:14)

我找到了一种使用SMO的方法:

Server srv; 
srv = new Server("ServerName"); 

Database db; 
db = srv.Databases["MyDatabase"]; 

var Params = db.StoredProcedures["MyStoredProc"].Parameters;

foreach(StoredProcedureParameter param in Params) {
    Console.WriteLine(param.Name + "-" + param.DefaultValue);
}

答案 1 :(得分:6)

在SQL Server 2005及以上版本中没什么大不了的:

SELECT 
    pa.NAME, 
    t.name 'Type',
    pa.max_length,
    pa.has_default_value,
    pa.default_value
FROM 
    sys.parameters pa
INNER JOIN 
    sys.procedures pr ON pa.object_id = pr.object_id
INNER JOIN 
    sys.types t ON pa.system_type_id = t.system_type_id
WHERE 
        pr.Name = 'YourStoredProcName'

<击>

不幸的是,即使这看起来像小蛋糕 - 它也不起作用: - (

来自Technet:

  

SQL Server 仅维护默认   此目录中的 CLR对象的值   视图;因此,这个专栏有一个   Transact-SQL对象的值为0 。   查看a的默认值   Transact-SQL对象中的参数,   查询的定义列   sys.sql_modules目录视图,或使用   OBJECT_DEFINITION系统功能。

所以你所能做的就是查询sys.sql_modules或调用SELECT object_definition(object_id)来基本上获取存储过程的SQL定义(T-SQL源代码),然后你需要解析那个(糟透了!大时间......)

似乎没有其他方法可以做到这一点...我很惊讶并且应用......

也许在SQL Server 2008 R2中? :-) 马克

答案 2 :(得分:1)

运行内置sp_help存储过程?

答案 3 :(得分:1)

对于存储过程,我相信您必须编写解析T-SQL或Microsoft提供的use the T-SQL parser的内容。

  

解析器和脚本生成器正常运行   在两个集会中。该   Microsoft.Data.Schema.ScriptDom   包含提供者不可知的类和   该   Microsoft.Data.Schema.ScriptDom.Sql   程序集包含用于的类   解析器和脚本生成器   SQL Server特定。

如何专门使用它来识别参数以及它们是否默认不会被覆盖,并且您将需要使用示例代码进行处理(可能需要付出一些努力)。

答案 4 :(得分:1)

这是一种黑客行为,但您可以随时为可选参数提供一个特殊名称,如:

@AgeOptional = 15

...然后编写一个简单的方法来检查参数是否可选。不理想,但鉴于这种情况,它实际上可能是一个不错的解决方案。

答案 5 :(得分:1)

这就是我做到的。从第一个参数开始直到AS语句,获取存储过程的一部分。使用declare语句创建一个临时存储过程,并返回所有参数id,名称,列类型(如果它们具有默认值)及其值的并集。然后执行存储过程,假设它们具有默认值的参数之间是否有相等的符号,如果它们没有默认值,我在执行期间将null传递给参数,并且读取结果集或者如果存在则填充存储过程一个临时表,以便我以后可以查询它。我检查了参数之间是否有任何相同的符号,如果是,最初我认为它们有默认值。如果有等号的注释等过程,这意味着它们没有默认值,在执行过程中我没有传递任何参数,执行失败,我抓到了错误信息,读取了参数名称并执行了这次我通过的程序null到参数。在程序中我使用了CLR字符串concat函数,因此如果直接执行它将无法编译,但你可以用XML路径替换,或者给我发回电子邮件,如果你想要我可以引导你完成clr 。因为我联合所有参数,我把它们作为varchar(max)

USE Util
GO
CREATE AGGREGATE [dbo].[StringConcat]
(@Value nvarchar(MAX), @Delimiter nvarchar(100))
RETURNS nvarchar(MAX)
EXTERNAL NAME [UtilClr].[UtilClr.Concat]
GO
CREATE FUNCTION dbo.GetColumnType (@TypeName SYSNAME,
                                  @MaxLength SMALLINT,
                                  @Precision TINYINT,
                                  @Scale TINYINT,
                                  @Collation SYSNAME,
                                  @DBCollation SYSNAME)
RETURNS TABLE
    AS
RETURN
    SELECT  CAST(CASE WHEN @TypeName IN ('char', 'varchar')
                      THEN @TypeName + '(' + CASE WHEN @MaxLength = -1 THEN 'MAX'
                                                  ELSE CAST(@MaxLength AS VARCHAR)
                                             END + ')' + CASE WHEN @Collation <> @DBCollation THEN ' COLLATE ' + @Collation
                                                              ELSE ''
                                                         END
                      WHEN @TypeName IN ('nchar', 'nvarchar')
                      THEN @TypeName + '(' + CASE WHEN @MaxLength = -1 THEN 'MAX'
                                                  ELSE CAST(@MaxLength / 2 AS VARCHAR)
                                             END + ')' + CASE WHEN @Collation <> @DBCollation THEN ' COLLATE ' + @Collation
                                                              ELSE ''
                                                         END
                      WHEN @TypeName IN ('binary', 'varbinary') THEN @TypeName + '(' + CASE WHEN @MaxLength = -1 THEN 'MAX'
                                                                                            ELSE CAST(@MaxLength AS VARCHAR)
                                                                                       END + ')'
                      WHEN @TypeName IN ('bigint', 'int', 'smallint', 'tinyint') THEN @TypeName
                      WHEN @TypeName IN ('datetime2', 'time', 'datetimeoffset') THEN @TypeName + '(' + CAST (@Scale AS VARCHAR) + ')'
                      WHEN @TypeName IN ('numeric', 'decimal') THEN @TypeName + '(' + CAST(@Precision AS VARCHAR) + ', ' + CAST(@Scale AS VARCHAR) + ')'
                      ELSE @TypeName
                 END AS VARCHAR(256)) AS ColumnType
GO
go
USE [master]
GO
IF OBJECT_ID('dbo.sp_ParamDefault') IS NULL 
    EXEC('CREATE PROCEDURE dbo.sp_ParamDefault AS SELECT 1 AS ID')
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE dbo.sp_ParamDefault
    @ProcName SYSNAME = NULL OUTPUT
AS 
SET NOCOUNT ON
SET ANSI_WARNINGS OFF
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

DECLARE @SQL VARCHAR(MAX),
    @ObjectId INT = OBJECT_ID(LTRIM(RTRIM(@ProcName))),
    @FirstParam VARCHAR(256),
    @LastParam VARCHAR(256),
    @SelValues VARCHAR(MAX),
    @ExecString VARCHAR(MAX),
    @WhiteSpace VARCHAR(10) = '[' + CHAR(10) + CHAR(13) + CHAR(9) + CHAR(32) + ']',
    @TableExists BIT = ABS(SIGN(ISNULL(OBJECT_ID('tempdb..#sp_ParamDefault'), 0))),
    @DeclareSQL VARCHAR(MAX),
    @ErrorId INT,
    @ErrorStr VARCHAR(MAX)

IF @ObjectId IS NULL 
    BEGIN
        SET @ProcName = NULL
        PRINT '/* -- SILENCE OPERATION --
IF OBJECT_ID(''tempdb..#sp_ParamDefault'') IS NOT NULL DROP TABLE #sp_ParamDefault
CREATE TABLE #sp_ParamDefault (Id INT, NAME VARCHAR(256), TYPE VARCHAR(256), HasDefault BIT, IsOutput BIT, VALUE VARCHAR(MAX))
*/

EXEC dbo.sp_ParamDefault
    @ProcName = NULL
'
RETURN
    END

SELECT  @SQL = definition,
        @ProcName = QUOTENAME(OBJECT_SCHEMA_NAME(@ObjectId)) + '.' + QUOTENAME(OBJECT_NAME(@ObjectId)),
        @FirstParam = FirstParam,
        @LastParam = LastParam
FROM    sys.all_sql_modules m (NOLOCK)
CROSS APPLY (SELECT MAX(CASE WHEN p.parameter_id = 1 THEN p.name
                        END) AS FirstParam,
                    Util.dbo.StringConcat(p.name, '%') AS Params
             FROM   sys.parameters p (NOLOCK)
             WHERE  p.object_id = m.OBJECT_ID) p
CROSS APPLY (SELECT TOP 1
                    p.NAME AS LastParam
             FROM   sys.parameters p (NOLOCK)
             WHERE  p.object_id = m.OBJECT_ID
             ORDER BY parameter_id DESC) l
WHERE   m.object_id = @ObjectId
IF @FirstParam IS NULL 
    BEGIN
        IF @TableExists = 0 
            SELECT  CAST(NULL AS INT) AS Id,
                    CAST(NULL AS VARCHAR(256)) AS Name,
                    CAST(NULL AS VARCHAR(256)) AS Type,
                    CAST(NULL AS BIT) AS HasDefault,
                    CAST(NULL AS VARCHAR(MAX)) AS VALUE
            WHERE   1 = 2
        RETURN
    END

SELECT  @DeclareSQL = SUBSTRING(@SQL, 1, lst + AsFnd + 2) + '
'
FROM    (SELECT PATINDEX ('%' + @WhiteSpace + @LastParam + @WhiteSpace + '%', @SQL) AS Lst) l
CROSS APPLY (SELECT SUBSTRING (@SQL, lst, LEN (@SQL)) AS SQL2) s2
CROSS APPLY (SELECT PATINDEX ('%' + @WhiteSpace + 'AS' + @WhiteSpace + '%', SQL2)  AS AsFnd) af


DECLARE @ParamTable TABLE (Id INT NOT NULL,
                           NAME SYSNAME NULL,
                           TYPE VARCHAR(256) NULL,
                           HasDefault BIGINT NULL,
                           IsOutput BIT NOT NULL,
                           TypeName SYSNAME NOT NULL) ;
WITH    pr
          AS (SELECT    p.NAME COLLATE SQL_Latin1_General_CP1_CI_AS AS ParameterName,
                        p.Parameter_id,
                        t.NAME COLLATE SQL_Latin1_General_CP1_CI_AS AS TypeName,
                        ct.ColumnType,
                        MAX(Parameter_id) OVER (PARTITION BY (SELECT 0)) AS MaxParam,
                        p.is_output
              FROM      sys.parameters p (NOLOCK)
              INNER JOIN sys.types t (NOLOCK) ON t.user_type_id = p.user_type_id
              INNER JOIN sys.databases AS db (NOLOCK) ON db.database_id = DB_ID()
              CROSS APPLY Util.dbo.GetColumnType(t.name, p.max_length, p.precision, p.scale, db.collation_name, db.collation_name) ct
              WHERE     OBJECT_ID = @ObjectId)
    INSERT  @ParamTable
            (Id,
             NAME,
             TYPE,
             HasDefault,
             IsOutput,
             TypeName)
            SELECT  Parameter_id AS Id,
                    ParameterName AS NAME,
                    ColumnType AS TYPE,
                    HasDefault,
                    is_output AS IsOutput,
                    TypeName
            FROM    pr a
            CROSS APPLY (SELECT ISNULL('%' + (SELECT Util.dbo.StringConcat (ParameterName, '%') FROM pr b WHERE b.parameter_id < a.parameter_id), '') + '%'
                                + ParameterName + '%=' + '%' + CASE WHEN parameter_id = MaxParam THEN @WhiteSpace + 'AS' + @WhiteSpace + '%'
                                                                    ELSE (SELECT Util.dbo.StringConcat (ParameterName, '%') FROM pr b
                                                                                    WHERE b.parameter_id > a.parameter_id) + '%'
                                                               END AS ptt) b
            CROSS APPLY (SELECT SIGN (PATINDEX (ptt, @DeclareSQL)) AS HasDefault) hd

AGAIN:
SELECT  @SelValues = CASE WHEN @TableExists = 1 THEN 'INSERT #sp_ParamDefault(Id, Name, Type, HasDefault, IsOutput, Value)
'                         ELSE ''
                     END + 'SELECT * FROM (VALUES' + Util.dbo.StringConcat('(' + CAST(Id AS VARCHAR) + ', ''' + Name + ''', ''' + Type + ''', '
                                                                           + CAST(HasDefault AS VARCHAR) + ', ' + CAST(IsOutput AS VARCHAR) + ', '
                                                                           + CASE WHEN TypeName NOT LIKE '%char%' THEN 'CAST(' + name + ' AS VARCHAR(MAX))'
                                                                                  ELSE name
                                                                             END + ')', ',
') + '
) d(Id, Name, Type, HasDefault, IsOutput, Value)',
        @ExecString = 'EXEC #sp_ParamDefaultProc
' + ISNULL(Util.dbo.StringConcat(CASE WHEN HasDefault = 0 THEN Name + ' = NULL'
                                 END, ',
'), '')
FROM    @ParamTable

SET @SQL = 'CREATE PROCEDURE #sp_ParamDefaultProc
' + SUBSTRING(@DeclareSQL, CHARINDEX(@FirstParam, @DeclareSQL), LEN(@DeclareSQL)) + '
' + @SelValues

IF OBJECT_ID('TEMPDB..#sp_ParamDefaultProc') IS NOT NULL 
    DROP PROCEDURE #sp_ParamDefaultProc
EXEC(@SQL)

BEGIN TRY
    EXEC(@ExecString)
END TRY
BEGIN CATCH
    SELECT  @ErrorStr = ERROR_MESSAGE(),
            @ErrorId = ERROR_NUMBER()
-- there must have been a comment containing equal sign between parameters
    UPDATE  p
    SET     HasDefault = 0
    FROM    (SELECT PATINDEX ('%expects parameter ''@%', @ErrorStr) AS ii) i
    CROSS APPLY (SELECT CHARINDEX ('''', @ErrorStr, ii + 20) AS uu) u
    INNER JOIN @ParamTable p ON p.name = SUBSTRING(@ErrorStr, ii + 19, uu - ii - 19)
    WHERE   ii > 0

    IF @@ROWCOUNT > 0 
        GOTO AGAIN

    RAISERROR(@ErrorStr, 16, 1)
    RETURN 30
END CATCH
GO
EXEC sys.sp_MS_marksystemobject 
    sp_ParamDefault
GO

答案 6 :(得分:1)

这是PowerShell中的SMO答案:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | out-null

$srv = New-Object "Microsoft.SqlServer.Management.Smo.Server" "MyServer\MyInstance"
$db = $srv.Databases["MyDatabase"];
$proc = $db.StoredProcedures["MyStoredProcedure"]

foreach($parameter in $proc.Parameters) {
  if ($parameter.DefaultValue){
     Write-Host "$proc ,  $parameter , $($parameter.DefaultValue)"
  }
  else{
     Write-Host "$proc ,  $parameter , No Default Value"
  }
 }