如何处理 T-SQL 存储过程中的多个结果集?

How to handle multiple result sets from a T-SQL stored procedure?

提问人:jreloz 提问时间:10/25/2023 最后编辑:Joel Coehoornjreloz 更新时间:10/25/2023 访问量:92

问:

我有一个返回多个结果集的 T-SQL 存储过程:

Select * from tbl1;
Select * from tbl2;
Select * from tbl3;

在我的 C# 代码中,我有这个:

using (SqlConnection con = new SqlConnection(_connectionString))
{
    con.Open();

    using (SqlCommand cmd = new SqlCommand("LIS.spData", con))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@ChargeId", chargeId);

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            List<DataTable> dtList = new List<DataTable>();

            while (reader.HasRows)
            {
                while (reader.Read())
                {
                    DataTable tmpdt = new DataTable();
                    tmpdt.Load(reader);
                    dtList.Add(tmpdt);  
                }

                reader.NextResult();
            }
        }
    }
}

在我的中它说的是真的,但我不知道为什么它不在我的数据表中存储任何数据。while(reader.HasRows)

有什么建议吗?

C# SQL-Server T-SQL ado.net

评论

0赞 HABO 10/25/2023
旁白:我想知道为什么 SqlDataReader.NextResult 方法说返回值:“ 如果有更多的结果集;否则。也许它是要检查的?truefalse

答:

0赞 vendettamit 10/25/2023 #1

您不需要调用 ,这是在方法内部完成的。只需在调用 Load 方法之前检查读取器中是否有可用的架构即可。 这是我是如何做到的(我需要创建一个异步版本的适配器。Fill() 方法):reader.Read()dataTable.Load(reader)

using (var reader = await command.ExecuteReaderAsync())
{
                var dataSet = new DataSet();
                do
                {
                    DataTable dataTable = new DataTable();

                    // Check if there's a schema specified in the reader before loading it into datatable
                    if (reader.FieldCount > 0)
                    {
                        dataTable.Load(reader);
                        dataSet.Tables.Add(dataTable);
                    }

                    // No schema..Skip reading and close the reader (it will only happen in case of executing a proc that has dynamic query and it doesn't have anything to return.
                    if (!reader.IsClosed && reader.FieldCount == 0)
                        reader.Close();
                }
                while (!reader.IsClosed);

                return dataSet;
}

评论

1赞 Joel Coehoorn 10/25/2023
这主要是很好的建议,但忽略了问题的重点(处理多个结果集)。
0赞 Albert D. Kallal 10/25/2023 #2

只需将结果发送到数据集,即可代替使用数据表。因此,您可以使用 .数据适配器的 Fill 方法。

数据集是表(数据表)的集合。

所以,说这个代码:

    void LoadData()
    {
        DataSet HotelInfo = new DataSet();
        string sConn = Properties.Settings.Default.TEST4;

        using (SqlConnection conn = new SqlConnection(sConn))
        {
            using (SqlCommand cmdSQL = new SqlCommand("HotelInfo", conn))
            {
                cmdSQL.CommandType = CommandType.StoredProcedure;
                cmdSQL.Parameters.Add("@Country", SqlDbType.NVarChar).Value = "Canada";

                SqlDataAdapter oReader = new SqlDataAdapter(cmdSQL);
                conn.Open();
                oReader.Fill(HotelInfo);
            }
        }

        // load grid of hotels
        GHotels.DataSource = HotelInfo.Tables[0];
        GHotels.DataBind(); 

        GPeople.DataSource = HotelInfo.Tables[1]; 
        GPeople.DataBind();

    }

因此,存储过程可以返回任意数量的表。在此示例中,假设有 2 张桌子(酒店,并假设可能被预订到酒店的人)。

T-SQL:T-SQL:

CREATE PROCEDURE [dbo].[HotelInfo] 
    -- Add the parameters for the stored procedure here
    @Country nvarchar(100) 
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    SELECT ID, City, HotelName, Description FROM tblHotelsA
    WHERE Country = @Country
    ORDER BY HotelName

    SELECT ID, FirstName, LastName, City FROM People
    ORDER BY Firstname

END

现在是标记:

        <div style="float:left;width:40%">
            <h3>Hotels</h3>
            <asp:GridView ID="GHotels" runat="server"
                CssClass="table"
                >
            </asp:GridView>
        </div>

        <div style="float:left;width:30%;margin-left:30px">
            <h3>People</h3>
            <asp:GridView ID="GPeople" runat="server"
                CssClass="table"
                >
            </asp:GridView>
        </div>

现在的结果是这样的:

enter image description here

因此,数据集只是表的集合,从存储过程返回的每个表都将成为数据集对象的表。

3赞 Joel Coehoorn 10/25/2023 #3

如果您想要的只是一个对象集合,那么切换到使用 an 到 a 的建议是正确的。(在现代 ASP.Net,早点适应也是件好事)。DataTableSqlDataAdapterFill()DataSetasync

但为了展示功能,你不需要做这些事情,使用是正确的。这里还有一些其他值得关注的事情。NextResult()

// Stack using blocks to reduce indentation
using (SqlConnection con = new(_connectionString))
using (SqlCommand cmd = new("LIS.spData", con))
{
    // avoid AddWithValue()
    cmd.Parameters.Add("@ChargeId", SqlDbType.Int).Value = chargeId;
    cmd.CommandType = CommandType.StoredProcedure;

    // wait to open the connection until the last possible moment
    con.Open();
    using (SqlDataReader reader = cmd.ExecuteReader())
    {
        List<DataTable> dtList = new();
        do
        {
            // No need to check rows or read from the DataReader to load a DataTable
            DataTable tmpdt = new();
            tmpdt.Load(reader);
            dtList.Add(tmpdt);  
        } while(reader.NextResult());
    }
}

下面是 DataSet 代码可能看起来的更简单版本:

// Gotta love target-typed new expressions
using (SqlConnection con = new(_connectionString))
using (SqlCommand cmd = new("LIS.spData", con))
using (SqlDataAdapter da = new(cmd))
{
    cmd.Parameters.Add("@ChargeId", SqlDbType.Int).Value = chargeId;
    cmd.CommandType = CommandType.StoredProcedure;
    DataSet result = new();
    // No need to even manually open the connection
    da.Fill(result);
}

最后,我想解决这个问题:

但我不知道为什么它不在我的数据表中存储任何数据。

该变量的声明范围非常小,我想知道它是否是后来重新声明的,而不是保留的。dtList