SQL Server:存储过程与多个结果集的问题

时间:2015-03-20 05:07:14

标签: c# sql sql-server entity-framework tsql

我试图从EF5中的存储过程中获取多个结果集。我试图减少我们平台中最昂贵的功能的数据库往返次数。

我们认为通过服务器发送事件进行更新。当视图数据更改时,每个用户都需要使用更新的数据接收其自定义视图。我们当前的方法是获取所有连接的用户,然后从每个用户的数据库中获取他们的视图。因为这非常昂贵,我们试图通过对存储过程执行一次调用来返回多个结果,从而使它更便宜一些。

我有以下代码:

var sql = "DECLARE @users UserList INSERT INTO @users(row) VALUES {0}" +
                  "EXEC dbo.GetTracklistViews(@users, '{1}');";
var sql_shard = "('{0}'),";
var sql_shard_last = "('{0}')";

var users = new List<Guid>();

//Logic to get all connected users
var builder = new StringBuilder();

for (var i = 0; i < users.Count; i++)
    if (i < (users.Count - 1))
       builder.Append(string.Format(sql_shard, users[i]));
    else
       builder.Append(string.Format(sql_shard_last, users[i]));

using (var ctx = new spottyData())
{
    ctx.Database.Initialize(force: false);
    ctx.Database.Connection.Open();

    using (var cmd = ctx.Database.Connection.CreateCommand())
    {
        cmd.CommandText = string.Format(sql, builder.ToString(), juke.ToString());

        var result = cmd.ExecuteReader();

        while (result.NextResult())
        {
            var set = ((IObjectContextAdapter)ctx)
                        .ObjectContext
                        .Translate<tracklist>(result, "tracklist", System.Data.Objects.MergeOption.NoTracking)
                        .ToArray();

            //Send event with 'set' data
        }
    }
}

var result = cmd.ExecuteReader();,它会抛出SqlException

  

@ users&#39;附近的语法不正确。

我们之前使用相同的语法在其他几个存储过程和函数中输入多个值。一切似乎都很好。我认为这与EXEC dbo.GetTracklistViews...有关。

如果你们中的任何一个人认为我们在这里尝试完全疯狂的事情,还有其他任何方法可以实现这一目标吗?或者我们是否必须寻找其他方法来削减昂贵的功能?

UserList类型的定义:

CREATE TYPE UserList
AS TABLE (
    [row] INT IDENTITY(1,1) PRIMARY KEY,
    [user_id] UNIQUEIDENTIFIER
);

1 个答案:

答案 0 :(得分:1)

首先 - 您不应该在字符串中使用逗号分隔的值列表作为参数。 SQL服务器解析和拆分它的效率非常低。相反,您的存储过程应使用Table值参数。

其次 - 尽量避免使用多个结果集。它非常凌乱。 由于您的所有结果集都是相同类型的表,因此我只创建一个新类(例如将其称为UserTrackList) - 具有与Tracklist相同的属性+ UserId的1个。它甚至可以从TrackList继承

例如:

要传入的表值类型:

CREATE TYPE GuidList
AS TABLE (
    [id] UNIQUEIDENTIFIER -- < assume thats what you're actually passing in?
);

存储过程定义

CREATE PROCEDURE dbo.GetTracklistViews
    @users GuidList READONLY,
    @juke NVARCHAR(1000) -- < I have no idea what this parameter is!
AS
BEGIN

-- SELECT a load of stuff - joined onto this table valued parameter?

END 

如何在C#中调用该过程

var sql = "EXEC dbo.GetTrackListViews @users, @juke";
var prms = new List<System.Data.SqlClient.SqlParameter>();

//build table valued parameter
var tbl = new DataTable();
tbl.Columns.Add(new DataColumn("id", typeof(guid));
foreach(var user in users) //assume these are guids.
{ tbl.Rows.Add(user) }
var prm1 = new System.Data.SqlClient.SqlParameter("users", SqlDbType.Structured);
prm1.Value = tbl;
prm1.TypeName = "GuidList";

prms.Add(prm1);

// other parameter is easy:
var prm2 = new System.Data.SqlClient.SqlParameter("juke", juke.ToString());
prms.Add(prm2);

List<UserTrackList> results;
using (var ctx = new spottyData())
{
    var query = ctx.Database.SqlQuery<UserTrackList>(sql, prms.ToArray());
    results = query.ToList();
    // NB - this works if the property names/types in the type "tracklist" 
    // match the column names/types in the returned resultset.
}

// example processing the results
foreach (var tracklist in results.GroupBy(x => x.UserId)
{
   user = users.First(x => x == tracklist.Key);
   //do something with the grouped result
}