简体   繁体   English

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

[英]SQL Server : issue with stored procedure with multiple result sets

I'm trying to get multiple result set from a stored procedure in EF5. 我正在尝试从EF5中的存储过程中获取多个结果集。 I'm trying to cut the number of database round-trips for the most expensive functions in our platform. 我正在尝试减少平台中最昂贵功能的数据库往返次数。

We have a view that updates through server sent events. 我们有一个通过服务器发送的事件进行更新的视图。 When the view data changes every user needs to receive their custom view with the updated data. 当视图数据更改时,每个用户都需要使用更新的数据来接收其自定义视图。 Our current approach is getting all connected users and then get their view from the database per user. 我们当前的方法是获取所有已连接的用户,然后从每个用户的数据库中获取他们的视图。 Because this is very expensive we were trying to make it a little bit cheaper by implementing one call to a stored procedure returning multiple results. 因为这是非常昂贵的,所以我们试图通过对存储过程执行一次调用以返回多个结果来使其便宜一些。

I have the following code: 我有以下代码:

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
        }
    }
}

On var result = cmd.ExecuteReader(); var result = cmd.ExecuteReader(); it throws an SqlException : 它抛出SqlException

Incorrect syntax near '@users'. '@users'附近的语法不正确。

We've used the same syntax to input multiple values in several other stored procedures and functions before. 之前,我们使用相同的语法在其他几个存储过程和函数中输入多个值。 Everything seems to be fine. 一切似乎都很好。 I think it has something to do with EXEC dbo.GetTracklistViews... . 我认为这与EXEC dbo.GetTracklistViews...

Also if any of you guys think we're trying to achieve something completely crazy here, is there any other way we can achieve this? 另外,如果你们中有人认为我们正在尝试在这里实现完全疯狂的目标,那么还有其他方法可以实现这一目标吗? Or do we have to go look for other ways to cut expensive functions? 还是我们必须寻找其他方法来削减昂贵的功能?

The definition of the UserList type: UserList类型的定义:

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

Firstly - you shouldn't be using a comma separated list of values in a string as a parameter. 首先-您不应该使用逗号分隔的字符串值列表作为参数。 Its pretty inefficient for SQL server to parse and split that. 对于SQL Server而言,解析和拆分它的效率非常低。 Instead your stored procedure should use a Table valued parameter. 而是,您的存储过程应使用Table值参数。

Secondly - try your hardest to avoid multiple result sets. 其次-尽最大努力避免出现多个结果集。 Its very messy. 非常混乱。 Since all your results sets are tables of the same type I'd just create a new class (call it UserTrackList for example) - with the same properties as Tracklist + 1 more for the UserId. 由于您的所有结果集都是相同类型的表,因此我将创建一个新类(例如,将其称为UserTrackList)-具有与Tracklist相同的属性,再加上1个UserId。 It could inherit from TrackList even 它甚至可以从TrackList继承

For example: 例如:

The table valued type to pass in: 要传递的表值类型:

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

The Stored procedure definition 存储过程定义

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 

How to call the procedure in C# 如何在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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM