Mengapa kolom dihitung TIDAK NULL dianggap dapat dibatalkan dalam tampilan?

15

Saya punya meja:

CREATE TABLE [dbo].[Realty](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [RankingBonus] [int] NOT NULL,
    [Ranking]  AS ([Id]+[RankingBonus]) PERSISTED NOT NULL
    ....
)

Dan sebuah pemandangan:

CREATE View  [dbo].[FilteredRealty] AS
 SELECT 
realty.Id as realtyId,
...
COALESCE(realty.Wgs84X, ruian_cobce.Wgs84X, ruian_obec.Wgs84X) as Wgs84X,
COALESCE(realty.Wgs84Y, ruian_cobce.Wgs84Y, ruian_obec.Wgs84Y) as Wgs84Y,
realty.Ranking,
...
FROM realty
JOIN Category ON realty.CategoryId = Category.Id
LEFT JOIN ruian_cobce ON realty.cobceId = ruian_cobce.cobce_kod
LEFT JOIN ruian_obec ON realty.obecId = ruian_obec.obec_kod
LEFT JOIN okres ON realty.okresId = okres.okres_kod
LEFT JOIN ExternFile ON realty.Id = ExternFile.ForeignId AND ExternFile.IsMain = 1
                     AND ExternFile.ForeignTable = 5
INNER JOIN Person ON realty.OwnerId = Person.Id
WHERE Person.ConfirmStatus = 1

Saya memiliki model dbml di C # (LinqToSQL) dengan tampilan FilteredRealty di dalamnya. Itu [Pemeringkatan] dikenali sebagai nullable int dan jadi saya harus memperbaiki jenis kode yang dihasilkan setiap kali ketika saya mengubah apa pun di database. Ini sangat menyebalkan bagi saya dan banyak pekerjaan manual.

Tidak ada agregat yang digunakan dalam FilteredRealty (mengenai pertanyaan terkait ini ).

Mengapa kolom Ranking pada tampilan dianggap nullable jika Realty.Ranking tidak dapat dibatalkan ?

Tomas Kubes
sumber

Jawaban:

19

The [Ranking]lapangan menunjukkan sebagai "Nullable" karena menjadi kolom dihitung. Ya, dinyatakan sebagai NOT NULL, tetapi sebagaimana halaman MSDN untuk Kolom yang Dikomputasi menyatakan, mesin basis data dapat mengubah tekad itu pada waktu permintaan:

Mesin Database secara otomatis menentukan pembatalan kolom yang dihitung berdasarkan ekspresi yang digunakan. Hasil dari sebagian besar ekspresi dianggap nullable bahkan jika hanya kolom yang tidak dapat dibatalkan hadir, karena kemungkinan underflow atau overflow akan menghasilkan hasil nol juga. Gunakan fungsi COLUMNPROPERTY dengan properti AllowsNull untuk menyelidiki nullability dari setiap kolom yang dihitung dalam tabel. Ekspresi yang nullable dapat diubah menjadi nonnullable dengan menetapkan ISNULL ( check_expression , constant ), di mana konstanta adalah nilai nonnull yang diganti dengan hasil null.

Jadi, mari kita lihat apakah ini benar:

CREATE TABLE [dbo].[Realty](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [RankingBonus] [int] NOT NULL,
    [Ranking]  AS ([Id]+[RankingBonus]) PERSISTED NOT NULL
);
GO

EXEC sp_help 'dbo.Realty';
-- Ranking: Nullable = "no"

SELECT COLUMNPROPERTY(OBJECT_ID(N'dbo.Realty'), N'Ranking', 'AllowsNull') AS [AllowsNull?];
-- 0

SELECT * FROM sys.dm_exec_describe_first_result_set(N'SELECT * FROM dbo.Realty', '', NULL);
-- Ranking: is_nullable = 1  ==  :-(

Sekarang mari kita lihat apakah saran mereka tentang ISNULLpekerjaan:

SELECT * FROM sys.dm_exec_describe_first_result_set(
   N'SELECT Id, RankingBonus, ISNULL(Ranking, -99) AS [RealRanking] FROM dbo.Realty;',
   '',
   NULL);
-- RealRanking: is_nullable = 0

Saran mereka tampaknya akurat, jadi mari kita coba terapkan pada definisi kolom yang dihitung:

ALTER TABLE dbo.Realty
  ADD [RankingFixed] AS (ISNULL(([Id]+[RankingBonus]), -99))
  PERSISTED NOT NULL;
GO

Dan sekarang kami memeriksa properti lagi, tetapi untuk bidang baru:

EXEC sp_help 'dbo.Realty';
-- RankingFixed: Nullable = "no"

SELECT COLUMNPROPERTY(OBJECT_ID(N'dbo.Realty'),
                      N'RankingFixed',
                      'AllowsNull') AS [AllowsNullsNow?];
-- 0

Sejauh ini terlihat positif, tetapi bahkan definisi asli melaporkan "BUKAN NULL" dari dua pemeriksaan ini. Jadi mari kita coba tes yang sebenarnya - bagaimana mesin database menentukan nullability pada saat run-time:

SELECT * FROM sys.dm_exec_describe_first_result_set(N'SELECT * FROM dbo.Realty', '', NULL);
-- RankingFixed: is_nullable = 0  ==  :-) WOO HOO!
Solomon Rutzky
sumber
13

Untuk menjamin bahwa ekspresi kolom yang dihitung Peringkat tidak menghasilkan NULL dalam kondisi apa pun, Anda harus membungkusnya ISNULLdengan nilai default yang sesuai. Sebagai contoh:

Ranking AS ISNULL(Id + RankingBonus, 0) PERSISTED NOT NULL

Itu NOT NULL kendala memastikan nilai bertahan tidak null, dalam konteks tabel-dan sesi-tingkat pengaturan berlaku ketika tabel tersebut dimodifikasi.

Namun, ketika kueri merujuk ekspresi itu, SQL Server memiliki pilihan antara menggunakan nilai tetap (jika pengaturan cocok) atau menghitung ekspresi baru.

Beberapa pengaturan sesi dapat menyebabkan overflow untuk mengembalikan NULL, misalnya, jadi SQL Server harus memperhitungkan kemungkinan ini. Ketika diakses melalui tampilan, SQL Server dengan benar menandai kolom sebagai berpotensi mengembalikan NULL.

Menggunakan terluar ISNULL ekspresi adalah satu-satunya cara yang didukung untuk mencapai apa yang Anda inginkan. Penggunaan COALESCEtidak akan berfungsi, misalnya.

Demo:

CREATE TABLE dbo.T1
(
    c1 integer NOT NULL,
    c2 integer NOT NULL,
    c3 AS c1 + c2 PERSISTED NOT NULL
);
GO
CREATE VIEW dbo.V1
AS
SELECT T.c1,
       T.c2,
       T.c3
FROM dbo.T1 AS T;
GO
SELECT AllowsNull = COLUMNPROPERTY(OBJECT_ID(N'dbo.V1', N'V'), N'c3', 'AllowsNull');
GO
ALTER TABLE dbo.T1
DROP COLUMN c3;
GO
ALTER TABLE dbo.T1
ADD c3 AS ISNULL(c1 + c2, 0) PERSISTED NOT NULL;
GO
EXECUTE sys.sp_refreshsqlmodule
    @name = N'dbo.V1';
GO
SELECT AllowsNull = COLUMNPROPERTY(OBJECT_ID(N'dbo.V1', N'V'), N'c3', 'AllowsNull');
GO
DROP VIEW dbo.V1;
DROP TABLE dbo.T1;
GO

Catat penggunaan sys.sp_refreshsqlmodulekarena tampilan Anda bukan schemabound.

Paul White 9
sumber