您所在的位置: 程序员家园 -> 家园博客 ->
 
在哪里摔倒
就在哪里自己爬起来

用户登录

查  找

最新评论

最新留言

常用网站

网易邮箱 GMAIL  

百度搜索 MSDN

霏凡软件 BT精品

影视帝国 射 手 网

电驴下载 全 库 网

友情连接

茄菲的窝 冰冰博客

枫叶飘零 玫  瑰

ACEN 云 豹 子

统  计



使用多记录集返回数据,避免多次执行sp_reset_connection
狼子 发表于 2007-8-22 11:20:00 阅读全文 | 回复(0) | 引用通告 | 编辑

先说一下sp_reset_connection,我对这个存储过程不是很理解,我是在ms sql的事件探查器里跟踪页面查到这个存储过程的,在F1里,有关这个存储过程只有一句话:

sp_reset_connection 存储过程由 SQL Server 用来支持事务中的远程存储过程调用。

msdn对系统存储过程(http://msdn2.microsoft.com/zh-cn/library/ms187961.aspx)的解释里,有这一段:

sp_reset_connection 存储过程由 SQL Server 用来支持事务中的远程存储过程调用。从连接池中重用连接时,该存储过程还将导致激发 Audit Login 和 Audit Logout 事件。

下面是F1中对Audit Login 和 Audit Logout 事件的解释:

Audit Login Event 收集自跟踪启动后发生的所有新的连接事件,例如客户端请求连接到运行 SQL Server 实例的服务器。

Audit Logout Event 收集自跟踪启动后发生的所有新的断开连接事件,如客户端发出断开连接命令。

在csdn的一个帖子《在sql server2000里是不是每次和数据库断开连接就会执行sp_reset_connection一次?》中(http://topic.csdn.net/t/20031224/01/2596230.html),我查到这一段话:

  Lostinet(Hello)回复于 2003-12-24 08:52:47
  不是   SqlServer的每次连接和断开,  
  而是SqlConnection的每次Open和Close  
   
  SqlServer的连接是持续的。  
  SqlConnection只是代理而已。  
  所以没当SqlConnection.Open一次,就调用sp_reset_connection来重新整理连接中的属性和变量。  
  否则可能和下次的时候产生冲突。

另外,还有一段英文解释《What does sp_reset_connection do?》(http://www.sqldev.net/misc/sp_reset_connection.htm),我载一段在这里:

sp_reset_connection resets the following aspects of a connection:

It resets all error states and numbers (like @@error)
It stops all EC's (execution contexts) that are child threads of a parent EC executing a parallel query
It will wait for any outstanding I/O operations that is outstanding
It will free any held buffers on the server by the connection
It will unlock any buffer resources that are used by the connection
It will release all memory allocated owned by the connection
It will clear any work or temporary tables that are created by the connection
It will kill all global cursors owned by the connection
It will close any open SQL-XML handles that are open
It will delete any open SQL-XML related work tables
It will close all system tables
It will close all user tables
It will drop all temporary objects
It will abort open transactions
It will defect from a distributed transaction when enlisted
It will decrement the reference count for users in current database; which release shared database lock
It will free acquired locks
It will releases any handles that may have been acquired
It will reset all SET options to the default values
It will reset the @@rowcount value
It will reset the @@identity value
It will reset any session level trace options using dbcc traceon()
sp_reset_connection will NOT reset:

Security context, which is why connection pooling matches connections based on the exact connection string
If you entered an application role using sp_setapprole, since application roles can not be reverted

我觉得,上面这么多关于sp_reset_connection的资料,阅读能力再糟糕的人,就是不可以完全理解这个存储过程,也应该想得到这个存储过程肯定是要占用不少资源的,对不对?

在asp.net2.0里,有各种数据源控件,SqlDataSource、ObjectDataSource,好几个,使用这些数据源控件很方便,只是拖拉,输入sql语句,就要以很方便的连接数据库,给控件添加上数据连接,方便到有点像PB的DataWindow了

就是,我一直没有想过这些控件是怎么运行的,我下面说的东西,只是我自己的想法,我不敢保证是正确的,我是使用代码测试出来的:

1、只要在页面里,加两个SqlDataSource,两个DropDownList,把两个DropDownList分别绑定到两个SqlDataSource,然后呢,启动事件探查器,刷新页面,我们就可以看见这个页面执行了两次sp_reset_connection

  在页面里,只要有一个数据源控件被有效执行,就会执行一次sp_reset_connection

2、在页面里,自己写代码读取数据库,只要执行一次dbConnect.Open();,sql就会执行一次sp_reset_connection

从这两种测试可以看出,Lostinet说的SqlConnection的每次Open和Close都会执行sp_reset_connection,是正确的

从上面《What does sp_reset_connection do?》也可以看出,这个存储过程,如果没有必要,可以省就省我想是最好的

有一种情况,就是字典表的使用,在一个页面里,如果FormView里的数据,对应的几个字段,都是读取字典表的DropDownList,这种情况经常有,就像一个员工的信息记录,里面一般都有学历、职称、职务等字段,学历、职称、职务这些,也经常都会做成字典表管理,所以,如果要管理(添加/修改)员工的信息,我们有时候会使用好几个数据源控件,一个数据源控件读取员工的信息,另外几个读取字典表数据

这时候,页面上有几个数据源控件,就会执行几次sp_reset_connection,这种情况是需要避免的

我想到的第一种方法是使用代码,设置SqlCommand,在连接一次数据库后,设置不同的CommandText,取不同的数据出来添加到DropDownList的ListItem中

昨天早上在msdn看到另一个文章《编写高性能 Web 应用程序的 10 个技巧》(http://www.microsoft.com/china/msdn/library/webservices/asp.net/us0501ASPNETPerformance.mspx?mfr=true)里面的第一条就是

技巧 1 — 返回多个结果集

仔细查看您的数据库代码,看是否存在多次进入数据库的请求路径。每个这样的往返都会降低应用程序可以提供的每秒请求数量。通过在一个数据库请求中返回多个结果集,可以节省与数据库进行通信所需的总时间长度。同时因为减少了数据库服务器管理请求的工作,还会使得系统伸缩性更强。

虽然可以使用动态 SQL 返回多个结果集,但是我首选使用存储过程。关于业务逻辑是否应该驻留于存储过程的问题还存在一些争议,但是我认为,如果存储过程中的逻辑可以约束返回数据的话(缩小数据集的大小、缩短网络上所花费时间,不必筛选逻辑层的数据),则应赞成这样做。

使用 SqlCommand 实例及其 ExecuteReader 方法填充强类型的业务类时,可以通过调用 NextResult 将结果集指针向前移动。图 1 显示了使用类型类填充几个 ArrayList 的示例会话。只从数据库返回您需要的数据将进一步减少服务器上的内存分配。

图1指向的连接是:http://msdn.microsoft.com/msdnmag/issues/05/01/ASPNETPerformance/default.aspx?fig=true#fig1

我测试了这部分代码,他是使用SqlDataReader读取多记录集的,我觉得这个技巧用在字典表上,就是避免多次执行sp_reset_connection的最好办法了

字典表的数据,我们总是要从数据库里读取出来的,所以整个页面,我们读取的数据量不会改变,就是,因为使用多记录集,我们只需要执行一次sp_reset_connection

下面给出我的测试代码:

GetMultiResultFromProc.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="GetMultiResultFromProc.aspx.cs" Inherits="GetMultiResultFromProc" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>无标题页</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <p>使用多记录集:</p>
        <p>用SqlDataAdapter提取数据</p>
        <asp:DropDownList ID="DropDownList1" runat="server">
        </asp:DropDownList>
        <asp:DropDownList ID="DropDownList2" runat="server">
        </asp:DropDownList><br />
        <br />用SqlDataReader读取数据
        <asp:DropDownList ID="DropDownList3" runat="server">
        </asp:DropDownList>
        <asp:DropDownList ID="DropDownList4" runat="server">
        </asp:DropDownList><br />
       
        <p>使用数据源:</p>
        <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
            SelectCommand="select ShipperID, CompanyName from Shippers"></asp:SqlDataSource>
        <asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
            SelectCommand="select RegionID, RegionDescription from Region"></asp:SqlDataSource>
        <asp:DropDownList ID="DropDownList5" runat="server" DataSourceID="SqlDataSource1"
            DataTextField="CompanyName" DataValueField="ShipperID">
        </asp:DropDownList>
        <asp:DropDownList ID="DropDownList6" runat="server" DataSourceID="SqlDataSource2"
            DataTextField="RegionDescription" DataValueField="RegionID">
        </asp:DropDownList>
    </div>
    </form>
</body>
</html>

GetMultiResultFromProc.aspx.cs

using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class GetMultiResultFromProc : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        GetData();
        DDLData();
    }

    #region 打开数据库
    private SqlConnection OpenConnect()
    {
        string connectString;
        connectString = ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString;
        SqlConnection dbConnect = new SqlConnection(connectString);
        if (!dbConnect.State.Equals(ConnectionState.Open))
        {
            dbConnect.Open();
        }
        return dbConnect;
    }
    #endregion

    #region 关闭数据库
    private void CloseConnect(SqlConnection dbConnect)
    {
        if (dbConnect.State.Equals(ConnectionState.Open))
        {
            dbConnect.Close();
        }
    }
    #endregion

    #region 用SqlDataReader读取多记录集void GetData()
    private void GetData()
    {
        //连接数据库
        SqlConnection dbConnect = OpenConnect();
        //设置命令
        SqlCommand dbCommand = new SqlCommand();
        dbCommand.Connection = dbConnect;
        dbCommand.CommandType = CommandType.StoredProcedure;
        dbCommand.CommandText = "a_northwind_OrdersPaged";
        //参数
        SqlParameter pIndex = new SqlParameter("@PageIndex", 1);
        SqlParameter pSize = new SqlParameter("@PageSize", 5);
        dbCommand.Parameters.Add(pIndex);
        dbCommand.Parameters.Add(pSize);
        //读取第一个结果集
        SqlDataReader dbReader;
        dbReader = dbCommand.ExecuteReader();
        while (dbReader.Read())
        {
            DropDownList3.Items.Add(new ListItem(dbReader[0].ToString(),dbReader[0].ToString()));
        }
        //读取第二个结果集
        dbReader.NextResult();
        while (dbReader.Read())
        {
            DropDownList4.Items.Add(new ListItem(dbReader[0].ToString(), dbReader[1].ToString()));
        }
        //关闭数据库
        dbReader.Close();
        CloseConnect(dbConnect);
    }
    #endregion

    #region 用SqlDataAdapter提取多记录集void DDLData()
    private void DDLData()
    {
        //设置相关信息
        string queryString;
        SqlConnection dbConnect = OpenConnect();
        queryString = "a_testMultiDataSet";
        SqlDataAdapter da = new SqlDataAdapter(queryString, dbConnect);
        DataSet ds = new DataSet("MultiDataSet");
        //填充数据
        try
        {
            //填充数据
            da.Fill(ds, "Multi");
            //绑定数据源
            DropDownList1.DataSource = ds.Tables[0].DefaultView;
            DropDownList2.DataSource = ds.Tables[1].DefaultView;
            //设置Text和Value
            DropDownList1.DataTextField = "CompanyName";
            DropDownList1.DataValueField = "ShipperID";
            DropDownList2.DataMember = "Region";
            DropDownList2.DataTextField = "RegionDescription";
            //绑定数据
            DropDownList1.DataBind();
            DropDownList2.DataBind();
        }
        catch (SqlException sqlex)
        {
            Response.Write(sqlex.Message);
        }
        finally
        {
            CloseConnect(dbConnect);
        }
    }
    #endregion
}

我也想过直接使用SqlDataSource提取数据,就是,因为SqlDataSource的Select方法返回的是DataView,我取不到数据集,所以全部使用了代码的方式,要自己手动打开数据库和关闭数据库

上面的代码,跟踪后得到的结果如下:

RPC:Completed        exec sp_reset_connection
RPC:Completed        exec a_northwind_OrdersPaged @PageIndex = 1, @PageSize = 5
RPC:Completed        exec sp_reset_connection
SQL:BatchCompleted   a_testMultiDataSet

RPC:Completed        exec sp_reset_connection
SQL:BatchCompleted   select ShipperID, CompanyName from Shippers
RPC:Completed        exec sp_reset_connection
SQL:BatchCompleted   select RegionID, RegionDescription from Region

红色部分,是我测试的用两种方法提取多记录集的数据,我没有在Page_Load里连接和关闭数据库,是因为我要测试这两种方法执行sp_reset_connection的情况

蓝色部分,就是两个SqlDataSource执行的情况,每个数据源控件,执行一次sp_reset_connection

现在可以看出来了,同样是绑定两个DropDownList的数据,使用数据源控件,要执行两次sp_reset_connection,使用多记录集,只需要执行一次

下面给出上面的代码用到的存储过程

数据库使用的是MS SQL自带的Northwind

create proc a_testMultiDataSet as
select ShipperID, CompanyName from Shippers
select RegionID, RegionDescription from Region

CREATE  PROCEDURE a_northwind_OrdersPaged
(
    @PageIndex int,
    @PageSize int
)
AS
BEGIN
DECLARE @PageLowerBound int
DECLARE @PageUpperBound int
DECLARE @RowsToReturn int

-- First set the rowcount
SET @RowsToReturn = @PageSize * (@PageIndex + 1)
SET ROWCOUNT @RowsToReturn

-- Set the page bounds
SET @PageLowerBound = @PageSize * @PageIndex
SET @PageUpperBound = @PageLowerBound + @PageSize + 1

-- Create a temp table to store the select results
CREATE TABLE #PageIndex
(
    IndexId int IDENTITY (1, 1) NOT NULL,
    OrderID int
)

-- Insert into the temp table
INSERT INTO #PageIndex (OrderID)
SELECT
    OrderID
FROM
    Orders
ORDER BY
    OrderID DESC

-- Return total count
SELECT COUNT(OrderID) as allOrder FROM Orders

-- Return paged results
SELECT
    O.*
FROM
    Orders O,
    #PageIndex PageIndex
WHERE
    O.OrderID = PageIndex.OrderID AND
    PageIndex.IndexID > @PageLowerBound AND
    PageIndex.IndexID < @PageUpperBound
ORDER BY
    PageIndex.IndexID

END

发表评论:

    昵称:
    密码:
    主页:
    标题:
Powered by Oblog.