【问题标题】:Reading cursor in C# from SQL Server's CURSOR parameter of stored procedure从存储过程的 SQL Server 的 CURSOR 参数读取 C# 中的游标
【发布时间】:2020-09-19 07:27:11
【问题描述】:

我在 Microsoft SQL Server 中有一个类似于以下内容的存储过程:

ALTER PROCEDURE [MySchema].[TestTable_MGR_RetrieveLaterThanDate]
    @TestDate DATETIME, 
    @TableData CURSOR VARYING OUTPUT
AS
BEGIN
    SET NOCOUNT ON;

    SET @TableData = CURSOR FOR 
                         SELECT *
                         FROM MySchema.TestTable
                         WHERE @TestDate <= test_date;

    OPEN @TableData;
END

我需要从 C# 调用它,但在创建保存输出光标数据所需的 SqlParameter 对象时遇到问题。

我正在创建的参数如下所示:

SqlParameter testDateParameter  = new SqlParameter();
testDateParameter.ParameterName = "@TestDate";
testDateParameter.Direction     = ParameterDirection.Input;
testDateParameter.SqlDbType     = SqlDbType.DateTime;
testDateParameter.Value         = theValue;

// I have no idea on what the correct SqlDbType should be here
SqlParameter tableDataParameter  = new SqlParameter();
tableDataParameter.ParameterName = "@TableData";
tableDataParameter.Direction     = ParameterDirection.Output;
tableDataParameter.SqlDbType     = SqlDbType.???;

我已经尝试(对于光标参数)SqlDbType.UdtSqlDbType.Structured,但是在这两种情况下,当调用 SqlCommandExecuteReader 方法时我都无法得到我想要的结果(两种情况下都有例外)。我尝试了这两个,因为我没有看到任何光标选项。

我知道通常不鼓励使用游标,但.NET 是否根本不允许从 SQL Server 存储过程中读取游标,或者我缺少什么?

提前感谢您的所有帮助。

【问题讨论】:

  • 坦率地说,如果这行得通,我会很惊讶,但是:你看到了什么例外?注意:您不会在此处使用 ExecuteReader,因为它实际上并没有发出选择 - 您只需使用 ExecuteNonQuery,但是......您实际上试图在这里 do 做什么?目标是什么?仅 SELECT 会出现什么问题(忘记 CURSOR)?
  • 使用 SqlDbType.Udt,您也需要 UdtTypeName,当然“CURSOR”是不允许的(例外是我没有使用 CURSOR 作为 UDT 类型名称的权限)。使用 SqlDbType.Structured,我得到了一个不同的错误:一个是大小(它希望我知道,在执行 sql 命令之前,游标中将包含多少行),另一个是 parameterdirection 输出不是支持 TableData。
  • 关于将 CURSOR 替换为 SELECT,这不是我们团队喜欢追求的:我们认为 SQL 代码只能在存储过程中,因此所有 select 语句只允许在存储中程序。不幸的是,这不是我们可以解决的问题。有趣的是,Oracle DLL(DataAccess 和 ManagedDataAccess)都允许使用 RefCursors,而且它们工作得很好,但 SQL Server 出于某种原因却不能。我觉得这非常奇怪。
  • “在存储过程中选择”——当然,没问题;我的意思是,它是主观的、有争议的和上下文相关的,但如果你喜欢它:很好——你喜欢。但这并不能解释为什么要为此使用 CURSOR。这是非常不寻常的,它不会让生活变得轻松。那么:为什么这里有一个 cursor 呢?你试图通过让你的proc直接发出select而不涉及光标来实现你无法做到的事情是什么?这就是几乎所有 API 的设计预期。这是 ExecuteReader 所期望的,例如,您似乎正在尝试使用哪个 API
  • 游标在几乎所有方面都受到积极的劝阻,所以:坦率地说,这对我来说并不是一个巨大的震惊;对于常规选择,我认为光标没有任何优点,并且有很多缺点

标签: c# sql-server stored-procedures cursor


【解决方案1】:

以下是Return Data from a Stored Procedure 文档的相关摘录:

游标数据类型不能通过以下方式绑定到应用程序变量 数据库 API,例如 OLE DB、ODBC、ADO 和 DB-Library。因为 必须先绑定 OUTPUT 参数,然后应用程序才能执行 过程,不能调用带有光标 OUTPUT 参数的过程 来自数据库 API。这些程序可以从 Transact-SQL 批处理、过程或触发器仅在游标时 OUTPUT 变量分配给 Transact-SQL 本地游标变量。

虽然文档没有特别提到SqlClient,但该注意事项适用于所有 SQL Server API。我相信这个限制是因为底层的 SQL Server TDS 协议不支持它。其他一些 DBMS 产品(例如 Oracle)的 ADO.NET 提供程序确实支持游标参数。

其他 SQL Server 客户端 API 确实有游标的概念,但它们是通过系统 API 存储过程而不是 T-SQL 实现的,使用服务器端语句句柄和客户端 API 方法来使用它们。 SqlClient,OTOH,旨在将数据流式传输回客户端,而不是维护服务器端游标状态。

虽然我不推荐这种技术,但您可以通过在一个存储过程中声明一个 T-SQL 全局游标并在同一连接上为 RBAR 调用另一个游标来避免游标输出参数。

CREATE OR ALTER PROCEDURE dbo.TestTable_MGR_RetrieveLaterThanDate
    @TestDate DATETIME
AS
SET NOCOUNT ON;
DECLARE TestTable_MGR_RetrieveLaterThanDate CURSOR GLOBAL FAST_FORWARD READ_ONLY
    FOR SELECT * -- consider an explicit column list here
        FROM MySchema.TestTable
        WHERE @TestDate <= test_date;
OPEN TestTable_MGR_RetrieveLaterThanDate;
GO

CREATE OR ALTER PROCEDURE dbo.FetchNext_TestTable_MGR_RetrieveLaterThanDate
AS
SET NOCOUNT ON;
FETCH NEXT FROM TestTable_MGR_RetrieveLaterThanDate;
IF @@FETCH_STATUS <> 0
BEGIN
    CLOSE TestTable_MGR_RetrieveLaterThanDate;
    DEALLOCATE TestTable_MGR_RetrieveLaterThanDate;
    RETURN @@FETCH_STATUS;
END;
GO

【讨论】:

    【解决方案2】:

    最终,CURSOR 不喜欢在 SQL Server 中这样使用,虽然在某些情况下种方法可以使用 CURSOR,但坦率地说,这几乎不是一个好主意.

    由于您尝试使用ExecuteReader,因此合乎逻辑的结论是:只需使用SELECT

    ALTER PROCEDURE [MySchema].[TestTable_MGR_RetrieveLaterThanDate]
        @TestDate DATETIME
    AS
    BEGIN
        SET NOCOUNT ON;
    
        SELECT *
        FROM MySchema.TestTable
        WHERE @TestDate <= test_date;
    END
    

    这对ExecuteReader 很好用。

    【讨论】:

    • 我个人会使用WHERE test_date &gt;= @TestDate,这对我来说更清楚,但这是主观的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多