MSDN Docs on table-valued Sql Clr functions状态:
Transact-SQL table-valued functions materialize the results of calling the function into an intermediate table. ... In contrast, CLR table-valued functions represent a streaming alternative. There is no requirement that the entire set of results be materialized in a single table. The IEnumerable object returned by the managed function is directly called by the execution plan of the query that calls the table-valued function, and the results are consumed in an incremental manner. ... It is also a better alternative if you have very large numbers of rows returned, because they do not have to be materialized in memory as a whole.
Then I find out that no data access is allowed in the 'Fill row' method 。这意味着您仍然必须在 init 方法中执行所有数据访问并将其保存在内存中,等待调用“Fill row”。我是不是误会了什么? 如果我不将结果强制放入数组或列表中,则会收到错误:“ExecuteReader 需要一个打开且可用的连接。”连接的当前状态已关闭。'
代码示例:
[<SqlFunction(DataAccess = DataAccessKind.Read, FillRowMethodName = "Example8Row")>]
static member InitExample8() : System.Collections.IEnumerable =
let c = cn() // opens a context connection
// I'd like to avoid forcing enumeration here:
let data = getData c |> Array.ofSeq
data :> System.Collections.IEnumerable
static member Example8Row ((obj : Object),(ssn: SqlChars byref)) =
do ssn <- new SqlChars(new SqlString(obj :?> string))
()
我在这里处理几百万行。有什么办法可以懒惰地做到这一点吗?
最佳答案
我假设您使用的是 SQL Server 2008。正如 Microsoft 员工在 this page 上提到的那样,2008 年要求使用 DataAccessKind.Read 标记的方法比 2005 年更加频繁。其中一个时间是 TVF 参与事务时(当我测试时,情况似乎总是如此)。解决方案是在连接字符串中指定 enlist=false
,可惜它不能与 context connection=true
结合使用。这意味着您的连接字符串需要采用典型的客户端格式:Data Source=.;Initial Catalog=MyDb;Integrated Security=sspi;Enlist=false
并且您的程序集必须使用 permission_set= 创建external_access
,至少。作品如下:
using System;
using System.Collections;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
namespace SqlClrTest {
public static class Test {
[SqlFunction(
DataAccess = DataAccessKind.Read,
SystemDataAccess = SystemDataAccessKind.Read,
TableDefinition = "RowNumber int",
FillRowMethodName = "FillRow"
)]
public static IEnumerable MyTest(SqlInt32 databaseID) {
using (var con = new SqlConnection("data source=.;initial catalog=TEST;integrated security=sspi;enlist=false")) {
con.Open();
using (var cmd = new SqlCommand("select top (100) RowNumber from SSP1 where DatabaseID = @DatabaseID", con)) {
cmd.Parameters.AddWithValue("@DatabaseID", databaseID.IsNull ? (object)DBNull.Value : databaseID.Value);
using (var reader = cmd.ExecuteReader()) {
while (reader.Read())
yield return reader.GetInt32(0);
}
}
}
}
public static void FillRow(object obj, out SqlInt32 rowNumber) {
rowNumber = (int)obj;
}
}
}
F# 中也有同样的事情:
namespace SqlClrTest
module Test =
open System
open System.Data
open System.Data.SqlClient
open System.Data.SqlTypes
open Microsoft.SqlServer.Server
[<SqlFunction(
DataAccess = DataAccessKind.Read,
SystemDataAccess = SystemDataAccessKind.Read,
TableDefinition = "RowNumber int",
FillRowMethodName = "FillRow"
)>]
let MyTest (databaseID:SqlInt32) =
seq {
use con = new SqlConnection("data source=.;initial catalog=TEST;integrated security=sspi;enlist=false")
con.Open()
use cmd = new SqlCommand("select top (100) RowNumber from SSP1 where DatabaseID = @DatabaseID", con)
cmd.Parameters.AddWithValue("@DatabaseID", if databaseID.IsNull then box DBNull.Value else box databaseID.Value) |> ignore
use reader = cmd.ExecuteReader()
while reader.Read() do
yield reader.GetInt32(0)
} :> System.Collections.IEnumerable
let FillRow (obj:obj) (rowNumber:SqlInt32 byref) =
rowNumber <- SqlInt32(unbox obj)
关于sql-server - CLR 表如何对函数 'streaming' 进行赋值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5653963/