web-dev-qa-db-ja.com

外部結合を使用して行バージョンを選択するとnullが返されないのはなぜですか

SQL Server 2014を使用します。

rowversion列のあるテーブル、結合する他のテーブル、および次のような選択:

 CREATE TABLE dbo.FooTable(
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [RowVersion] [rowversion] NULL,
 CONSTRAINT [PK_FooTable] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)) 

 CREATE TABLE dbo.BarTable(
    [Id] [int] IDENTITY(1,1) NOT NULL
 CONSTRAINT [PK_BarTable] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)) 

Insert into BarTable default values
GO


SELECT *
  FROM FooTable ft
  FULL OUTER JOIN BarTable bt on ft.Id = Bt.Id

選択の結果は次のとおりです。

Id  RowVersion  Id
NULL    0x      1

BarTableと一致しない場合、RowVersion列はnullではなく、0xです。

これは、アプリサーバーで結果をデシリアライズするために使用するDapperを混乱させ、行のFooTable部分のオブジェクト(空のバイト配列のみを含む無効なオブジェクト)を構築する必要があると考えます。

この予想外の振る舞いには何らかの理由がありますか?

SQLステートメントでCASEを使用して値をnullに変換し直すか、アプリケーションコードで処理するよりも優れた解決策はありますか?

6
Igand

これは、.NETのSqlClientのバグのようです。結果でnullとマークされていても、列は空のbyte[]として返されます。

例えば

using System;
using System.Data.Odbc;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Threading;
using System.Threading.Tasks;
using System.Transactions;

namespace ConsoleApp14
{
    class Program
    {

        static void Main(string[] args)
        {

            using (var con = new SqlConnection("Server=localhost;database=tempdb;Integrated Security=true"))
            {
                con.Open();
                var cmd = con.CreateCommand();
                cmd.CommandText = "select cast(null as rowversion) rv";
                using (var rdr = cmd.ExecuteReader())
                {
                    rdr.Read();
                    var allowDbNull = rdr.GetColumnSchema()[0].AllowDBNull;
                    var isNull = rdr.IsDBNull(0);
                    var val = rdr[0];

                    Console.WriteLine($"SqlClient: AllowDbNull {allowDbNull} IsDbNull: {isNull} {val.GetType().Name} {val}");

                }
            }


            using (var con = new SqlConnection("Server=localhost;database=tempdb;Integrated Security=true"))
            {
                con.Open();
                var cmd = con.CreateCommand();
                cmd.CommandText = "select @val = cast(null as rowversion) ";

                var p = cmd.Parameters.Add(new SqlParameter("@val", System.Data.SqlDbType.Timestamp));
                p.Direction = System.Data.ParameterDirection.Output;

                cmd.ExecuteNonQuery();
                {

                   SqlBinary val = (SqlBinary) p.SqlValue;
                   Console.WriteLine($"SqlClient (parameter): IsDbNull: {val.IsNull} {val.GetType().Name} {val}");

                }
            }

            using (var con = new OdbcConnection("Driver={ODBC Driver 17 for SQL Server};Server=localhost;Trusted_Connection=yes"))
            {
                con.Open();
                var cmd = con.CreateCommand();
                cmd.CommandText = "select cast(null as rowversion) rv";
                using (var rdr = cmd.ExecuteReader())
                {
                    rdr.Read();
                    var allowDbNull = rdr.GetSchemaTable().Rows[0]["AllowDBNull"];
                    var isNull = rdr.IsDBNull(0);
                    var val = rdr[0];

                    Console.WriteLine($"ODBC:      AllowDbNull {allowDbNull} IsDbNull: {isNull} {val.GetType().Name} {val}");

                }

            }


        }
    }
}

アウトプット

SqlClient: AllowDbNull True IsDbNull: False Byte[] System.Byte[]
SqlClient (parameter): IsDbNull: True SqlBinary Null
ODBC:      AllowDbNull True IsDbNull: True DBNull

https://github.com/dotnet/SqlClient/issues/255 で問題をオープンしましたが、WontFixとしてクローズされる可能性があります。ソースのメモによると

// Dev10 Bug #479607 - this should have been the same as SqlDbType.Binary, but it's a rejected breaking change

この問題は発生していますが、重大な変更として修正されていません。いずれにせよ、重大な変更でいっぱいの.NET Coreで修正され、.NET Frameworkにそのまま残される可能性があります。

「バグです」という正しい承認された回答を除いて、あなたができることは、ypercubeがコメントで提案するものです:VARBINARYにキャストしてください

SELECT ft.[Id], 
       CAST(ft.[RowVersion] AS varbinary(8)),
       bt.[Id] 
FROM FooTable ft 
     FULL OUTER JOIN BarTable bt
     ON ft.Id = Bt.Id ;
2
Igand