Home > Net >  Query returns different result from C# then from SSMS
Query returns different result from C# then from SSMS

Time:03-19

I have this weird problem that I cannot explain

When I run a certain query from a C# application, it returns a different result then when I run it in SSMS, but not when I use parameters

The command is this

select dbo.fnGetPlaceID('6025', null, null)

It returns -1 when run from the c# application, and returns 1489 when run from SSMS

But if I do this

declare @DealerCode varchar(30) = '6025'
declare @RegionCode varchar(50) = null
declare @MerkID int = null
select dbo.fnGetPlaceID(@DealerCode, @RegionCode, @MerkID)

Then it returns the correct value 1489 also from the c# application

So what is the difference ?

Additional code :

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

And the code in 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);
                    }
                }
            }
        }
    }
}

What I have tried

Then I tried this change in the sql function

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

And now it does works correct from the c# application.

So, for some reason when I call this function from a c# application it seems that @@ROWCOUNT is not equal to 1, and when I call this function from SSMS then @@ROWCOUNT is equal to 1.
I have not confirmed this, but that is my conclusion.
The question remains, why ?
And also why does it works different when I use parameters ?

I have fixed my problem by altering the function to

select @Count = count(1) 
from   relation.tblRelation r 
where  r.DealerCode = @DealerCode

and then use the variable @Count in stead of rowcount, but I would still like some explanation of what is happening here.

I also checked what would happen to the first query if more than one row is found.
Because I put the result of the query in a variable, that should fail if more than one row is found (An error in my function, I know, I fixed it by now)

declare @Result int = -1

select @Result = r.RelationID
from   relation.tblRelation r
where  r.dealercode is null

select @@ROWCOUNT

But in stead of throwing an exception what I expected, rowcount just had a value of 4512

EDIT

As suggested by @DanGuzman it works when I put TSQL_SCALAR_UDF_INLINING to OFF

select dbo.fnGetPlaceID('6025', null, null)
OPTION (USE HINT('DISABLE_TSQL_SCALAR_UDF_INLINING'))

EDIT 2

It also works without the hint if I do this ALTER DATABASE [myDataBase] SET COMPATIBILITY_LEVEL = 140; It was on 150

CodePudding user response:

There are a number of issues with scalar function inlining that have been fixed since the initial SQL Server 2019 RTM release. Install the latest cumulative update to get these fixes and others as well.

Without patching, work-arounds include:

  • disable inlining with query hint OPTION (USE HINT('DISABLE_TSQL_SCALAR_UDF_INLINING'))
  • disable inlining at the database level with USE YourDatabase;ALTER DATABASE SCOPED CONFIGURATION SET TSQL_SCALAR_UDF_INLINING = OFF;
  • Change the database compatibility level to 140 or lower

The latest SQL Server patch download links are listed on this page

Regarding different behavior from C# code and SSMS, this is due to different execution plans. SSMS queries specify the ARITHABORT ON session setting by default whereas C# does not set the option. Different session settings may yield different execution plans and make it difficult to troubleshoot. See Erland Sommarskog's article for details. I'll add that SSMS sets ARITHABORT only for backwards compatibility. It's on by default anyway in the SQL 2019 world because database compatibility level 80 (SQL 2000) is no longer supported. Consider unchecking the SET ARITHABORT option in SSMS under Tools-->Options-->Query Execution-->SQL Server--Advanced.

  • Related