从 C# 查询 returns 与 SSMS 不同的结果
Query returns different result from C# then from SSMS
我有一个无法解释的奇怪问题
当我 运行 来自 C# 应用程序的某个查询时,它 returns 与我在 SSMS 中 运行 它时的结果不同,但当我使用参数时 [=20] =]
命令是这样的
select dbo.fnGetPlaceID('6025', null, null)
它 returns -1 当 运行 来自 c# 应用程序时, returns 1489 当 运行 来自 SSMS
但如果我这样做
declare @DealerCode varchar(30) = '6025'
declare @RegionCode varchar(50) = null
declare @MerkID int = null
select dbo.fnGetPlaceID(@DealerCode, @RegionCode, @MerkID)
然后它 returns 正确的值 1489 也来自 c# 应用程序
那有什么区别呢?
附加代码:
CREATE function dbo.fnGetPlaceID(@DealerCode varchar(30), @RegionCode varchar(50), @MerkID int) returns int as
begin
declare @Result int = -1
if @RegionCode = ''
set @RegionCode = null
select @Result = r.RelationID
from relation.tblRelation r
where r.dealercode = @DealerCode
-- in the example of '6025' there is only one row, I checked this
-- So the code below should not be executed
if @@ROWCOUNT <> 1
begin
select @Result = rel.RelationID
from relation.tblRelation rel
left outer join dbo.tblRegionCodes r on rel.RegionCodeID = r.FordRegionCodeID
where rel.dealercode = @DealerCode
and (@RegionCode is null or r.RegionCode = @RegionCode)
and (@MerkID is null or rel.BrandID = @MerkID)
if @@ROWCOUNT <> 1
set @Result = -1
end
return @Result
end
和c#中的代码
FillDataTable(myDataTable, "select dbo.fnGetPlaceID('6025', null, null)")
public void FillDataTable(DataTable table, string SqlText, int commandTimeOut = 300)
{
if (_ConnectionString != null && _ConnectionString != "")
{
using (SqlConnection connection = new SqlConnection(_ConnectionString))
{
OpenConnection(connection, SqlText);
using (SqlCommand command = new SqlCommand(SqlText, connection))
{
command.CommandType = CommandType.Text;
command.CommandTimeout = commandTimeOut;
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
try
{
adapter.Fill(table);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
}
}
}
}
我试过的
然后我尝试在 sql 函数中进行此更改
alter function dbo.fnGetPlaceID(@DealerCode varchar(30), @RegionCode varchar(50), @MerkID int) returns int as
begin
declare @Result int = -1
if @RegionCode = ''
set @RegionCode = null
select @Result = r.RelationID
from relation.tblRelation r
where r.dealercode = @DealerCode
/*
-- in the example of '6025' there is only one row, I checked this
-- So the code below should not be executed
if @@ROWCOUNT <> 1
begin
select @Result = rel.RelationID
from relation.tblRelation rel
left outer join dbo.tblRegionCodes r on rel.RegionCodeID = r.FordRegionCodeID
where rel.dealercode = @DealerCode
and (@RegionCode is null or r.RegionCode = @RegionCode)
and (@MerkID is null or rel.BrandID = @MerkID)
if @@ROWCOUNT <> 1
set @Result = -1
end
*/
return @Result
end
现在它在 c# 应用程序中确实可以正常工作。
因此,出于某种原因,当我从 C# 应用程序调用此函数时,@@ROWCOUNT 似乎不等于 1,而当我从 SSMS 调用此函数时,@@ROWCOUNT 等于 1。
我没有证实这一点,但这是我的结论。
问题仍然存在,为什么?
还有为什么当我使用参数时它的工作方式不同?
我已通过将函数更改为
解决了我的问题
select @Count = count(1)
from relation.tblRelation r
where r.DealerCode = @DealerCode
然后使用变量@Count 代替 rowcount,但我仍然想对这里发生的事情做一些解释。
我还检查了如果找到多个行,第一个查询会发生什么情况。
因为我将查询结果放在一个变量中,所以如果找到不止一行,它应该会失败(我知道我的函数中有一个错误,我现在已经修复了它)
declare @Result int = -1
select @Result = r.RelationID
from relation.tblRelation r
where r.dealercode is null
select @@ROWCOUNT
但是并没有像我预期的那样抛出异常,rowcount 的值为 4512
编辑
正如@DanGuzman 所建议的那样,当我将 TSQL_SCALAR_UDF_INLINING 设置为 OFF
时它会起作用
select dbo.fnGetPlaceID('6025', null, null)
OPTION (USE HINT('DISABLE_TSQL_SCALAR_UDF_INLINING'))
编辑 2
如果我这样做,它也可以在没有提示的情况下工作ALTER DATABASE [myDataBase] SET COMPATIBILITY_LEVEL = 140;
它是在 150
最终更新
我今天安装了 CU15,并在兼容级别设置为 150 的情况下再次进行了测试,现在它也可以在 c# 中正常运行。所以这个方案现在确认了。
自初始 SQL Server 2019 RTM 版本以来,标量函数内联存在许多问题,这些问题已得到修复。安装最新的累积更新以获得这些修复和其他修复。
不打补丁,work-arounds包括:
- 禁用查询提示内联
OPTION (USE HINT('DISABLE_TSQL_SCALAR_UDF_INLINING'))
- 使用
USE YourDatabase;ALTER DATABASE SCOPED CONFIGURATION SET TSQL_SCALAR_UDF_INLINING = OFF;
在数据库级别禁用内联
- 将数据库兼容级别更改为 140 或更低
最新的SQL服务器补丁下载链接列在this page
关于 C# 代码和 SSMS 的不同行为,这是由于不同的执行计划。 SSMS 查询默认指定 ARITHABORT ON
会话设置,而 C# 不设置该选项。不同的会话设置可能会产生不同的执行计划,详情请参阅 difficult to troubleshoot. See Erland Sommarskog's 文章。我要补充一点,SSMS 设置 ARITHABORT 只是为了向后兼容。在 SQL 2019 世界中,它默认处于启用状态,因为不再支持数据库兼容级别 80 (SQL 2000)。考虑在工具-->选项-->查询执行-->SQL 服务器--高级下取消选中 SSMS 中的 SET ARITHABORT
选项。
我有一个无法解释的奇怪问题
当我 运行 来自 C# 应用程序的某个查询时,它 returns 与我在 SSMS 中 运行 它时的结果不同,但当我使用参数时 [=20] =]
命令是这样的
select dbo.fnGetPlaceID('6025', null, null)
它 returns -1 当 运行 来自 c# 应用程序时, returns 1489 当 运行 来自 SSMS
但如果我这样做
declare @DealerCode varchar(30) = '6025'
declare @RegionCode varchar(50) = null
declare @MerkID int = null
select dbo.fnGetPlaceID(@DealerCode, @RegionCode, @MerkID)
然后它 returns 正确的值 1489 也来自 c# 应用程序
那有什么区别呢?
附加代码:
CREATE function dbo.fnGetPlaceID(@DealerCode varchar(30), @RegionCode varchar(50), @MerkID int) returns int as
begin
declare @Result int = -1
if @RegionCode = ''
set @RegionCode = null
select @Result = r.RelationID
from relation.tblRelation r
where r.dealercode = @DealerCode
-- in the example of '6025' there is only one row, I checked this
-- So the code below should not be executed
if @@ROWCOUNT <> 1
begin
select @Result = rel.RelationID
from relation.tblRelation rel
left outer join dbo.tblRegionCodes r on rel.RegionCodeID = r.FordRegionCodeID
where rel.dealercode = @DealerCode
and (@RegionCode is null or r.RegionCode = @RegionCode)
and (@MerkID is null or rel.BrandID = @MerkID)
if @@ROWCOUNT <> 1
set @Result = -1
end
return @Result
end
和c#中的代码
FillDataTable(myDataTable, "select dbo.fnGetPlaceID('6025', null, null)")
public void FillDataTable(DataTable table, string SqlText, int commandTimeOut = 300)
{
if (_ConnectionString != null && _ConnectionString != "")
{
using (SqlConnection connection = new SqlConnection(_ConnectionString))
{
OpenConnection(connection, SqlText);
using (SqlCommand command = new SqlCommand(SqlText, connection))
{
command.CommandType = CommandType.Text;
command.CommandTimeout = commandTimeOut;
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
try
{
adapter.Fill(table);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
}
}
}
}
我试过的
然后我尝试在 sql 函数中进行此更改
alter function dbo.fnGetPlaceID(@DealerCode varchar(30), @RegionCode varchar(50), @MerkID int) returns int as
begin
declare @Result int = -1
if @RegionCode = ''
set @RegionCode = null
select @Result = r.RelationID
from relation.tblRelation r
where r.dealercode = @DealerCode
/*
-- in the example of '6025' there is only one row, I checked this
-- So the code below should not be executed
if @@ROWCOUNT <> 1
begin
select @Result = rel.RelationID
from relation.tblRelation rel
left outer join dbo.tblRegionCodes r on rel.RegionCodeID = r.FordRegionCodeID
where rel.dealercode = @DealerCode
and (@RegionCode is null or r.RegionCode = @RegionCode)
and (@MerkID is null or rel.BrandID = @MerkID)
if @@ROWCOUNT <> 1
set @Result = -1
end
*/
return @Result
end
现在它在 c# 应用程序中确实可以正常工作。
因此,出于某种原因,当我从 C# 应用程序调用此函数时,@@ROWCOUNT 似乎不等于 1,而当我从 SSMS 调用此函数时,@@ROWCOUNT 等于 1。
我没有证实这一点,但这是我的结论。
问题仍然存在,为什么?
还有为什么当我使用参数时它的工作方式不同?
我已通过将函数更改为
解决了我的问题select @Count = count(1)
from relation.tblRelation r
where r.DealerCode = @DealerCode
然后使用变量@Count 代替 rowcount,但我仍然想对这里发生的事情做一些解释。
我还检查了如果找到多个行,第一个查询会发生什么情况。
因为我将查询结果放在一个变量中,所以如果找到不止一行,它应该会失败(我知道我的函数中有一个错误,我现在已经修复了它)
declare @Result int = -1
select @Result = r.RelationID
from relation.tblRelation r
where r.dealercode is null
select @@ROWCOUNT
但是并没有像我预期的那样抛出异常,rowcount 的值为 4512
编辑
正如@DanGuzman 所建议的那样,当我将 TSQL_SCALAR_UDF_INLINING 设置为 OFF
时它会起作用select dbo.fnGetPlaceID('6025', null, null)
OPTION (USE HINT('DISABLE_TSQL_SCALAR_UDF_INLINING'))
编辑 2
如果我这样做,它也可以在没有提示的情况下工作ALTER DATABASE [myDataBase] SET COMPATIBILITY_LEVEL = 140;
它是在 150
最终更新
我今天安装了 CU15,并在兼容级别设置为 150 的情况下再次进行了测试,现在它也可以在 c# 中正常运行。所以这个方案现在确认了。
自初始 SQL Server 2019 RTM 版本以来,标量函数内联存在许多问题,这些问题已得到修复。安装最新的累积更新以获得这些修复和其他修复。
不打补丁,work-arounds包括:
- 禁用查询提示内联
OPTION (USE HINT('DISABLE_TSQL_SCALAR_UDF_INLINING'))
- 使用
USE YourDatabase;ALTER DATABASE SCOPED CONFIGURATION SET TSQL_SCALAR_UDF_INLINING = OFF;
在数据库级别禁用内联
- 将数据库兼容级别更改为 140 或更低
最新的SQL服务器补丁下载链接列在this page
关于 C# 代码和 SSMS 的不同行为,这是由于不同的执行计划。 SSMS 查询默认指定 ARITHABORT ON
会话设置,而 C# 不设置该选项。不同的会话设置可能会产生不同的执行计划,详情请参阅 difficult to troubleshoot. See Erland Sommarskog's 文章。我要补充一点,SSMS 设置 ARITHABORT 只是为了向后兼容。在 SQL 2019 世界中,它默认处于启用状态,因为不再支持数据库兼容级别 80 (SQL 2000)。考虑在工具-->选项-->查询执行-->SQL 服务器--高级下取消选中 SSMS 中的 SET ARITHABORT
选项。