10

我有一个使用sp_helptext检索存储过程文本的应用程序。它适用于我所有的存储过程,除了CLR 存储过程。如果我尝试sp_helptext在 SQLCLR 存储过程上使用,我会收到以下错误:

对象“PROC_NAME”没有文本

我知道当我使用“script as”->“create to”-> 命令时 SSMS 可以做到这一点。

但是,当我使用 SQL Server Profiler 跟踪 SSMS“生成脚本”操作时,它给了我一个非常复杂的活动列表。如果必须的话,我可以通过它,但是有人知道以编程方式获取 CLR 存储过程代码的简单方法吗?

编辑澄清
我不想从程序集中看到实际代码;我只是要求一种简单的方法来查看T-SQL代码,如下例所示:

CREATE PROCEDURE [dbo].[MY_PROC]
    @PARAM1 [xml],
    @PARAM2 [uniqueidentifier],
    @PARAM3 [nvarchar](255),
    @PARAM4[bit] = False
WITH EXECUTE AS OWNER
AS
EXTERNAL NAME [SomeSolution.SomeProject].[SomeAssembly].[SomeMethod]
GO

换言之,SQLCLR 函数的“SQL Server 端”。

4

9 回答 9

3

我有同样的困境,并在网上一遍又一遍地搜索任何解决方案来获取 CLR 存储过程的代码。最后不得不像你说的那样分析SSMS“生成脚本”动作做了什么,这就是我得到的:

--GET ALL CLR stored procedures
SELECT
sp.name AS [Name],
sp.object_id AS [object_ID],
case when amsp.object_id is null then N'''' else asmblsp.name end AS [AssemblyName],
case when amsp.object_id is null then N'''' else amsp.assembly_class end AS [ClassName],
case when amsp.object_id is null then N'''' else amsp.assembly_method end AS [MethodName]
FROM
sys.all_objects AS sp
LEFT OUTER JOIN sys.assembly_modules AS amsp ON amsp.object_id = sp.object_id
LEFT OUTER JOIN sys.assemblies AS asmblsp ON asmblsp.assembly_id = amsp.assembly_id
LEFT OUTER JOIN sys.procedures AS spp ON spp.object_id = sp.object_id
WHERE spp.type like 'PC'

--For each CLR SP get the parameters in use
SELECT
param.name AS [Name]
FROM
sys.all_objects AS sp
INNER JOIN sys.all_parameters AS param ON param.object_id=sp.object_id
WHERE sp.name like 'your_sp_name' order by param.parameter_id ASC

--For each parameter get the values, data type and so on...
SELECT
param.name AS [Name],
param.parameter_id AS [param_ID],
sp.object_id AS [object_ID],
param.default_value AS [DefaultValue],
usrt.name AS [DataType],
sparam.name AS [DataTypeSchema],
ISNULL(baset.name, N'''') AS [SystemType],
CAST(CASE WHEN baset.name IN (N'nchar', N'nvarchar') AND param.max_length <> -1 THEN         param.max_length/2 ELSE param.max_length END AS int) AS [Length],
CAST(param.precision AS int) AS [NumericPrecision],
CAST(param.scale AS int) AS [NumericScale]
FROM
sys.all_objects AS sp
INNER JOIN sys.all_parameters AS param ON param.object_id=sp.object_id
LEFT OUTER JOIN sys.types AS usrt ON usrt.user_type_id = param.user_type_id
LEFT OUTER JOIN sys.schemas AS sparam ON sparam.schema_id = usrt.schema_id
LEFT OUTER JOIN sys.types AS baset ON (baset.user_type_id = param.system_type_id and     baset.user_type_id = baset.system_type_id) 
WHERE param.name='@param1' and sp.name='your_sp_name'

使用这个脚本,我制作了一个 Perl 脚本来为我生成代码。我想从这里你可以做同样的事情或创建自己的存储过程来打印所需的代码。我不是 SQL 程序员,所以我不知道该怎么做,但是如果有人对上述查询进行编程,请分享。

于 2012-07-24T16:27:38.900 回答
2

CLR 存储过程不会包含文本,因为它是指向 DLL 的链接。您需要获取 DLL 的源代码

例如,请参阅公共语言运行时 (CLR) 集成编程概念

于 2010-11-09T16:48:01.370 回答
2

简短的回答:不,你不能那样做。(至少不是以编程方式/轻松地)CLR 过程是从 .NET 程序集(二进制文件)加载的,并且没有简单的方法来获取此类文件的源代码。不在 SQL Server 中。但是您可以使用RedGate Reflector 之类的工具来反汇编 DLL 并查看/恢复存储过程的源代码。

于 2010-11-09T16:57:50.077 回答
2
  1. 与类似的 T-SQL 对象不同,CREATE [ STORED PROCEDURE | FUNCTION | TRIGGER | TYPE | AGGREGATE ] ...SQLCLR 对象的语句不是按原样存储的;它们是通过对象类型和sys.assembliessys.assembly_modulessys.parameterssys.types和的组合派生的sys.assembly_types

    如果您尝试CREATE用 T-SQL 或其他一些非 .NET 语言构建这些语句,那么您需要从这些表中选择适当的数据并将它们拼凑在一起。但是,如果您使用的是 .NET,则可以使用SMO 库 ( Microsoft.SqlServer.Smo.dll )中的Scripter 类。它应该能够在 SQL Server 中编写任何脚本。事实上,您可能应该使用它来为您的工具编写所有内容,而不是使用查询。

  2. 除了在 SSMS 中动态查看存储过程/函数/触发器定义(即快速简单)之外,您不应该使用它sp_helptext来提取对象定义。它做了很多不必要的工作,因为它似乎自 SQL Server 2000 以来只进行了少量更新。它的两个主要问题是:

    1. 它不处理长度大于 4000 个字符的单个行
    2. 它只允许 CRLF (ie \r\n) 换行符,而不是 LF-only 换行符 (ie \n) 是有效的。

    您可以通过查看该系统存储过程的定义来了解这一点:

    EXEC sp_helptext 'sp_helptext';
    

    相反,您应该使用OBJECT_DEFINITION()内置函数(它接受一个object_id作为输入)或从包含以下定义的系统目录视图之一中进行选择:sys.sql_modulessys.server_sql_modulessys.system_sql_modulessys.system_sql_modules

于 2017-02-06T16:11:09.697 回答
1

我们遇到了未将更改添加到源代码管理的问题,因此我执行了以下操作来获取更新。

  1. 使用 sqlservermanager 我查询了所有 sys.assembly_files 以找到我需要的内容。
  2. 编写了一个快速控制台程序来获取已更改文件的源代码。

    • 在我们的例子中,它是针对 assembly_id 为 68541 的记录和多个文件的 file_id 都大于 3

    • cs 文件的源位于名为 content 的列中,文件名位于名为 name 的列中。我将内容转换为字节数组,然后转换为字符串,并使用 filename.txt 将内容写入文本文件。

    • 复制并粘贴更新的代码并推送到源代码控制。

      using (SqlConnection conn = new SqlConnection(connstring))
      {
          using (SqlCommand cmd = new SqlCommand("SELECT * FROM sys.assembly_files WHERE assembly_id = 68541 and file_id > 3", conn))
          {
              DataTable ds = new DataTable();
              using (SqlDataAdapter da = new SqlDataAdapter(cmd))
              {
                  da.Fill(ds);
                  foreach (DataRow r in ds.Rows)
                  {
                      byte[] binaryString = (byte[])r["content"];
                      string x = Encoding.UTF8.GetString(binaryString);
                      string filename = @"C:\SQLCLR";
                      string filePath = string.Format(@"{0}\{1}.txt", filename,r["name"]);
                      File.WriteAllText(filePath, x);
      
                  }
              }
          }
      }
      
于 2014-04-07T22:03:21.287 回答
0

实际上,您可以这样做 - 通过检查sys.assembly_files目录视图:

SELECT CONVERT(VARCHAR(MAX), content) as my_source_code 
FROM sys.assembly_files
于 2012-02-24T18:04:12.937 回答
0

要提取组件,请运行以下命令:

DECLARE @IMG_PATH VARBINARY(MAX)
DECLARE @ObjectToken INT

SELECT @IMG_PATH = content FROM sys.assembly_files WHERE assembly_id = 65536

EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT
    EXEC sp_OASetProperty @ObjectToken, 'Type', 1
    EXEC sp_OAMethod @ObjectToken, 'Open'
    EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @IMG_PATH
    EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, 'D:\SqlServerProject1.dll', 2
    EXEC sp_OAMethod @ObjectToken, 'Close'
    EXEC sp_OADestroy @ObjectToken

然后使用 Redgate Reflector 或任何 .Net 反编译器(ilspy,...)对其进行反编译

于 2014-07-28T09:31:44.417 回答
0

这个问题很老了,但互联网上没有其他来源,只是说明如何使用 SSMS 脚本。

作为 Alex Castillo,我还对 SSMS 脚本程序进行了概要分析和逆向工程,然后为了方便起见将它们全部组合到一个 T-SQL 查询中。这是我得到的:

SELECT 
    'DROP ' +
    CASE o.[type]
        WHEN 'FT' THEN 'FUNCTION'
        WHEN 'FS' THEN 'FUNCTION'
        WHEN 'PC' THEN 'PROCEDURE'
    END + ' [' + SCHEMA_NAME(ao.[schema_id]) + '].[' + o.[name] + '];' AS [drop_statement]
    ,'CREATE ' +
    CASE o.[type]
        WHEN 'FT' THEN 'FUNCTION'
        WHEN 'FS' THEN 'FUNCTION'
        WHEN 'PC' THEN 'PROCEDURE'
    END + ' [' + SCHEMA_NAME(ao.[schema_id]) + '].[' + o.[name] + ']  ' +
    CASE o.[type]
        WHEN 'FT' THEN '(' + COALESCE(inp.[params_def], '') + ') RETURNS TABLE (' + oc.[columns_def] + ')'
        WHEN 'FS' THEN '(' + COALESCE(inp.[params_def], '') + ') RETURNS ' + t.[name] + CASE WHEN t.[name] IN ('char', 'varchar', 'binary', 'varbinary', 'nchar', 'nvarchar', 'datetime2') THEN '(' + CASE WHEN t.[name] IN ('nchar', 'nvarchar') AND aop.[max_length] != -1 THEN CAST(aop.[max_length] / 2 AS [varchar]) WHEN t.[name] IN ('nvarchar') AND aop.[max_length] = -1 THEN 'MAX' WHEN t.[name] = 'datetime2' THEN CAST(aop.[scale] AS [varchar]) ELSE CAST(aop.[max_length] AS [varchar]) END  + ')' WHEN t.[name] IN ('numeric', 'decimal') THEN '(' + CAST(aop.[precision] AS [varchar]) + ', ' + CAST(aop.[scale] AS [varchar]) + ')' ELSE '' END
        WHEN 'PC' THEN COALESCE(inp.[params_def], '')
    END + ' WITH EXECUTE AS CALLER AS ' +
    ' EXTERNAL NAME [' + a.[name] COLLATE SQL_Latin1_General_CP1_CI_AS + '].[' + am.[assembly_class] + '].[' + am.[assembly_method] + '];' AS [create_statement]
FROM sys.assemblies a WITH(NOLOCK)
JOIN sys.assembly_modules am WITH(NOLOCK) ON a.[assembly_id] = am.[assembly_id]
JOIN sys.objects o WITH(NOLOCK) ON am.[object_id] = o.[object_id]
LEFT JOIN sys.all_parameters AS aop WITH(NOLOCK) ON aop.[object_id] = am.[object_id] AND aop.[is_output] = 1
LEFT JOIN sys.types AS t WITH(NOLOCK) ON (t.[user_type_id] = aop.[system_type_id] AND t.[user_type_id] = t.[system_type_id]) OR ((t.[system_type_id] = aop.[system_type_id]) AND (t.[user_type_id] = aop.[user_type_id]) AND (t.[is_user_defined] = 0) AND (t.[is_assembly_type] = 1)) 
JOIN sys.all_objects ao WITH(NOLOCK) ON o.[object_id] = ao.[object_id]
OUTER APPLY (
    SELECT
        SUBSTRING((SELECT
        ', ' + aip.[name] + ' ' + it.[name] + CASE WHEN it.[name] IN ('char', 'varchar', 'binary', 'varbinary', 'nchar', 'nvarchar', 'datetime2') THEN '(' + CASE WHEN it.[name] IN ('nchar', 'nvarchar') AND aip.[max_length] != -1 THEN CAST(aip.[max_length] / 2 AS [varchar]) WHEN it.[name] IN ('nvarchar') AND aip.[max_length] = -1 THEN 'MAX' WHEN it.[name] = 'datetime2' THEN CAST(aip.[scale] AS [varchar]) ELSE CAST(aip.[max_length] AS [varchar]) END  + ')' WHEN it.[name] IN ('numeric', 'decimal') THEN '(' + CAST(aip.[precision] AS [varchar]) + ', ' + CAST(aip.[scale] AS [varchar]) + ')' ELSE '' END AS [params_def]
    FROM sys.all_parameters AS aip WITH(NOLOCK)
    LEFT JOIN sys.types AS it WITH(NOLOCK) ON (it.[user_type_id] = aip.[system_type_id] AND it.[user_type_id] = it.[system_type_id]) OR ((it.[system_type_id] = aip.[system_type_id]) AND (it.[user_type_id] = aip.[user_type_id]) AND (it.[is_user_defined] = 0) AND (it.[is_assembly_type] = 1)) 
    WHERE 1 = 1
        AND aip.[is_output] = 0
        AND aip.[object_id] = am.[object_id]
    ORDER BY aip.[parameter_id]
    FOR XML PATH, TYPE).value('.[1]', 'nvarchar(MAX)'), 3, 2147483647) AS [params_def]
) AS inp
OUTER APPLY (
    SELECT 
        SUBSTRING((SELECT
            ', [' + ac.[name] + '] ' + ct.[name] + CASE WHEN ct.[name] IN ('char', 'varchar', 'binary', 'varbinary', 'nchar', 'nvarchar', 'datetime2') THEN '(' + CASE WHEN ct.[name] IN ('nchar', 'nvarchar') AND ac.[max_length] != -1 THEN CAST(ac.[max_length] / 2 AS [varchar]) WHEN ct.[name] IN ('nvarchar') AND ac.[max_length] = -1 THEN 'MAX' WHEN ct.[name] = 'datetime2' THEN CAST(ac.[scale] AS [varchar]) ELSE CAST(ac.[max_length] AS [varchar]) END  + ')' WHEN ct.[name] IN ('numeric', 'decimal') THEN '(' + CAST(ac.[precision] AS [varchar]) + ', ' + CAST(ac.[scale] AS [varchar]) + ')' ELSE '' END + CASE ac.[is_nullable] WHEN 0 THEN ' NOT' ELSE '' END + ' NULL'
        FROM sys.all_columns ac
        LEFT JOIN sys.types AS ct WITH(NOLOCK) ON (ct.[user_type_id] = ac.[system_type_id] AND ct.[user_type_id] = ct.[system_type_id]) OR ((ct.[system_type_id] = ac.[system_type_id]) AND (ct.[user_type_id] = ac.[user_type_id]) AND (ct.[is_user_defined] = 0) AND (ct.[is_assembly_type] = 1)) 
        WHERE 1 = 1
            AND ac.[object_id] = am.[object_id]
        ORDER BY ac.[column_id]
        FOR XML PATH, TYPE).value('.[1]', 'nvarchar(MAX)'), 3, 2147483647) AS [columns_def]
) AS oc
WHERE 1 = 1
    AND a.[name] = <'your assembly name'>
    AND SCHEMA_NAME(ao.[schema_id]) = <'your schema name'>

只需执行查询并将相应输出行中的值复制粘贴到新的查询窗口中并执行。该查询涵盖了很多方面,但不包括用户定义类型(如果您的 SQL CLR 例程中有任何类型)。调整起来并不难,我只是不在我的代码中使用 SQL CLR UDT。我希望有人会发现它有用。

于 2020-01-20T21:27:11.123 回答
-3

它非常简单 - 如果您可以访问 SQL Server Management Studio。

右键单击 CLR 存储过程,然后选择 CREATE SCRIPT - 瞧。

快乐的 CLR。

-丹麦语。

于 2013-09-25T10:13:43.043 回答