SqlDataAdapter.FillSchema SQL 2017 年未获取 DataTable

SqlDataAdapter.FillSchema not getting DataTable on SQL 2017

我不知道为什么,但是当我在 MSSQL 2012 和 2015 上执行该代码时一切正常(我正在将数据Table 的架构接收到声明的数据集),而当我在 MSSQL 2017 上执行此操作时 - FillSchema 对数据集没有任何作用。

DataSet.Tables.Count 等于 0.

下面是示例代码:

var da = new SqlDataAdapter();
var cn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["Test"].ConnectionString);
try
{
    cn.Open();
    var sql = "SELECT * FROM MyTable T" +
        "WHERE T.MyTableId=123 AND " +
        "EXISTS" +
        "(" +
        "  SELECT * FROM dbo.MyFunction() CTX WHERE ISNULL(T.MyOtherId, -1) = CTX.MyOtherId" +
        ")";
    da.SelectCommand = new SqlCommand(sql, cn);

    var ds = new DataSet();
    da.FillSchema(ds, SchemaType.Source);
    //There no table in the DataSet!
}
catch (Exception ex)
{
    MessageBox.Show("Error " + ex.Message);
}
finally
{
    cn.Close();
}

MS SQL 2017 有什么变化吗?或者我做错了什么?

编辑 1: 当我从查询中删除 EXISTS 条件时,问题就消失了。我认为调用函数可能是原因。

编辑 2: 当我将查询简化为:

var sql = "SELECT * FROM dbo.MyFunction()";

仍然无法正常工作...您可能想知道 SQL 中的 MyFunction 做了什么。这是一个多语句 Table 函数:

CREATE FUNCTION dbo.MyFunction ()
RETURNS @RESULT TABLE 
(
    ID BIGINT NOT NULL,
    NAME VARCHAR(50),
    CODE VARCHAR(50)
)
AS
BEGIN
    INSERT INTO @RESULT VALUES(1, 'Name', 'NAME')
RETURN 
END
GO

当我将 Multi-Statement Table 函数重写为 Inline-Statement 时,它起作用了。 我知道这不是 .NET 中接收模式的最佳方式...但是知道为什么多语句函数不起作用吗?

发生这种情况是因为 SQL 在 SET FMTONLY ON, which is still the method used by ADO.NET to retrieve metadata. It's been deprecated, and sp_describe_first_result_set does work correctly for this case, but that's still no reason for SQL Server 2017 to fail. This has been reported as a bug 下执行 SELECT * FROM dbo.MyFunction() 时,Server 2017 没有 return 元数据。

推测:这个错误可能是作为新 interleaved execution feature 的副作用引入的,它改变了这些函数的执行方式。如果数据库兼容级别下降到 130,它就会消失,这会禁用它(除其他外),这一点得到了加强。我不知道有一个跟踪标志只禁用交错执行,可以用来测试这个。

有一种变通方法:即使在兼容级别 140 下,set fmtonly on; exec ('select * from dbo.MyFunction()'); set fmtonly off 也会 return 元数据,因此使用 EXEC 获取查询的元数据仍然有效。在上面,

var sql = "EXEC ('SELECT * FROM dbo.MyFunction()')";

应该 return 结果。