.net 如何确定SQL Server存储过程参数是否具有默认值?
是否有方法通过编程确定SQL Server存储过程参数是否具有默认值?(如果可以确定默认值,则可获得额外积分。)甚至不尝试 提前感谢您的帮助 编辑:老实说,我不在乎它是否是SQL查询、SMO对象等。运行内置存储过程?在SQL Server 2005及更高版本中没什么大不了的:.net 如何确定SQL Server存储过程参数是否具有默认值?,.net,sql-server,stored-procedures,.net,Sql Server,Stored Procedures,是否有方法通过编程确定SQL Server存储过程参数是否具有默认值?(如果可以确定默认值,则可获得额外积分。)甚至不尝试 提前感谢您的帮助 编辑:老实说,我不在乎它是否是SQL查询、SMO对象等。运行内置存储过程?在SQL Server 2005及更高版本中没什么大不了的: SELECT pa.NAME, t.name 'Type', pa.max_length, pa.has_default_value, pa.default_value FROM
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。
查看
Transact-SQL对象中的参数,
查询
sys.sql_模块目录视图,或使用
对象定义系统函数
因此,您所能做的就是查询sys.sql\u modules
或调用SELECT object\u definition(object\u id)
基本上获取存储过程的sql定义(T-sql源代码),然后您需要解析它(糟透了!!big time…)
似乎真的没有其他方法可以做到这一点…我感到惊讶和震惊
可能在SQL Server 2008 R2中?:-)
Marc我找到了一种使用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);
}
对于存储过程,我相信您必须编写解析T-SQL或Microsoft提供的内容 解析器和脚本生成器处于活动状态 在两个集合中。这个
Microsoft.Data.Schema.ScriptDom
包含提供程序不可知类和
这个
Microsoft.Data.Schema.ScriptDom.Sql
程序集包含用于
语法分析器和脚本生成器
特定于SQL Server
如何具体地使用它来识别参数,以及它们是否为默认值,还没有涉及,这将是您必须使用示例代码来解决的问题(可能需要付出大量的努力)。这是一种黑客行为,但您始终可以为可选参数指定一个特殊名称,如: @年龄可选=15
…然后编写一个简单的方法,检查参数是否为可选参数。不太理想,但考虑到目前的情况,这可能是一个不错的解决方案。我就是这么做的。获取存储过程的一部分,从第一个参数开始,一直到AS语句。使用declare语句创建了一个临时存储过程,并返回所有参数ID、名称、列类型(如果它们具有默认值)及其值。然后执行存储过程,假设它们具有默认值的参数之间是否有等号,如果它们没有默认值,我在执行过程中将null传递给参数,然后读取结果集,或者如果存在,则存储过程填充一个临时表,以便稍后查询它。我检查了参数之间是否有任何等号,如果是,最初我假设它们有默认值。如果有一个带有等号的注释等,则表示该过程没有默认值,并且在执行过程中,我没有传递任何参数,执行失败,我捕获了错误消息,读取了参数名称,并执行了该过程。这次,我将null传递给参数。在这个过程中,我使用了一个CLR字符串concat函数,因此,如果直接执行,它将不会编译,但您可能可以用XML路径等替换,或者给我发电子邮件,如果您愿意,我可以引导您通过CLR。因为我联合了所有参数,所以我将它们转换为varchar(max)
使用Util
去
创建聚合[dbo]。[StringConcat]
(@Value-nvarchar(最大值),@Delimiter-nvarchar(100))
返回nvarchar(最大值)
外部名称[UtilClr]。[UtilClr.Concat]
去
创建函数dbo.GetColumnType(@TypeName SYSNAME,
@MaxLength SMALLINT,
@精密罐头,
@鳞片罐头,
@排序规则SYSNAME,
@数据库排序规则(SYSNAME)
返回表
作为
返回
选择CAST(当@TypeName在('char','varchar'中时为大小写)
然后是@TypeName+”(“+当@MaxLength=-1时的大小写,然后是'MAX'
ELSE CAST(@MaxLength作为VARCHAR)
结束+')+当@Collation@DBCollation时使用大小写,然后使用“COLLATE”+@Collation
否则“
结束
当@TypeName在('nchar','nvarchar'中时)
然后是@TypeName+”(“+当@MaxLength=-1时的大小写,然后是'MAX'
ELSE CAST(@MaxLength/2为VARCHAR)
结束+')+当@Collation@DBCollation时使用大小写,然后使用“COLLATE”+@Collation
否则“
结束
当@TypeName在('binary','varbinary')中时,则为@TypeName+'('+当@MaxLength=-1时,则为'MAX'时的大小写)
ELSE CAST(@MaxLength作为VARCHAR)
结束+“)”
当@TypeName在('bigint','int','smallint','tinyint')中时,则@TypeName
当@TypeName处于('datetime2','time','datetimeoffset')时,则@TypeName+'('+CAST(@Scale AS VARCHAR)+')
当@TypeName为('numeric','decimal')时,则为@TypeName+'('+CAST(@Precision AS VARCHAR)+','+CAST(@Scale AS VARCHAR)+')
ELSE@TypeName
结束为VARCHAR(256))作为ColumnType
去
去
U
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
[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"
}
}