Bagaimana saya bisa membantu SQL Server mengenali kolom tampilan yang diindeks TIDAK BISA?

9

Saya memiliki tampilan indeks berikut yang didefinisikan dalam SQL Server 2008 (Anda dapat mengunduh skema kerja dari intisari untuk tujuan pengujian):

CREATE VIEW dbo.balances
WITH SCHEMABINDING
AS
SELECT
      user_id
    , currency_id

    , SUM(transaction_amount)   AS balance_amount
    , COUNT_BIG(*)              AS transaction_count
FROM dbo.transactions
GROUP BY
      user_id
    , currency_id
;
GO

CREATE UNIQUE CLUSTERED INDEX UQ_balances_user_id_currency_id
ON dbo.balances (
      user_id
    , currency_id
);
GO

user_id,, currency_iddan transaction_amountsemuanya didefinisikan sebagai NOT NULLkolom dalam dbo.transactions. Namun, ketika saya melihat definisi tampilan di Object Explorer Management Studio, itu menandai kedua balance_amountdan transaction_countsebagai NULL-able kolom dalam tampilan.

Saya telah melihat beberapa diskusi, yang ini adalah yang paling relevan dari mereka, yang menyarankan beberapa pengocokan fungsi dapat membantu SQL Server mengenali bahwa kolom tampilan selalu NOT NULL. Namun, tidak ada pengocokan seperti itu dalam kasus saya, karena ekspresi pada fungsi agregat (misalnya a ISNULL()atas SUM()) tidak diperbolehkan dalam tampilan yang diindeks.

  1. Apakah ada cara saya dapat membantu SQL Server mengenali itu balance_amountdan transaction_countdapat NOT NULLdigunakan?

  2. Jika tidak, haruskah saya memiliki keprihatinan tentang kolom-kolom ini yang secara keliru diidentifikasi sebagai NULL-able?

    Dua masalah yang bisa saya pikirkan adalah:

    • Setiap objek aplikasi yang dipetakan ke tampilan saldo mendapatkan definisi saldo yang salah.
    • Dalam kasus yang sangat terbatas, pengoptimalan tertentu tidak tersedia untuk Pengoptimal Kueri karena tidak memiliki jaminan dari pandangan bahwa kedua kolom ini NOT NULL.

    Apakah salah satu dari masalah ini merupakan masalah besar? Apakah ada masalah lain yang harus saya ingat?

Nick Chammas
sumber
Ya ada kekhawatiran, misalnya ORM Anda akan membuat jenis yang dapat dibatalkan, yang pada gilirannya akan membutuhkan perhatian ekstra dalam kode saat menggunakannya, yang tidak berguna (atau bahkan menyesatkan) dalam kasus Anda.
Marcel
Ini juga tampaknya menjadi masalah dalam cyt rekursif ketika berulang pada bidang non-nullable (tidak ada agregat) meskipun IsNull (..., 0) pada akhirnya dapat menyembuhkan.
crokusek

Jawaban:

10

user_id,, currency_iddan transaction_amountsemuanya didefinisikan sebagai NOT NULLkolom dalamdbo.transactions

Tampak bagi saya bahwa SQL Server memiliki asumsi selimut bahwa agregat dapat menghasilkan nullbahkan jika bidang yang dioperasikannya adalah not null. Ini jelas benar dalam kasus-kasus tertentu:

create table foo(bar integer not null);
select sum(bar) from foo
-- returns 1 row with `null` field

Dan juga benar dalam versi umum group bysukacube

Case uji yang lebih sederhana ini mengilustrasikan poin bahwa setiap agregat diartikan sebagai nullable:

CREATE VIEW dbo.balances
with schemabinding
AS
SELECT
      user_id
    , sum(1)   AS balance_amount
FROM dbo.transactions
GROUP BY
      user_id
;
GO

IMO ini adalah batasan (walaupun kecil) dari SQL Server - beberapa RDBMS lain memungkinkan pembuatan batasan tertentu pada pandangan yang tidak ditegakkan dan ada hanya untuk memberikan petunjuk kepada pengoptimal, meskipun saya pikir 'keunikan' lebih cenderung untuk membantu dalam menghasilkan rencana permintaan yang baik daripada 'nullability'


Jika nullability kolom itu penting, mungkin untuk digunakan dengan ORM, pertimbangkan untuk membungkus tampilan yang diindeks di tampilan lain yang hanya menjamin non-nullability menggunakan ISNULL:

CREATE VIEW dbo.balancesORM
WITH SCHEMABINDING
AS
SELECT 
    B.[user_id],
    B.currency_id,
    balance_amount = ISNULL(B.balance_amount, 0),
    transaction_count = ISNULL(B.transaction_count, 0)
FROM dbo.balances AS B;

Rincian Penjelajah Objek SSMS

Jack mengatakan coba topanswers.xyz
sumber
5

Saya tidak berpikir ada cara Anda dapat memaksa SQL Server untuk mengenali kolom-kolom ini sebagai tidak dapat dibatalkan, meskipun jelas tidak. Anda dapat mencoba mengubah urutan bagaimana Anda mendefinisikan ISNULL/ COALESCEsekitar ekspresi di dalam SUM() , misalnya, tetapi itu tidak akan membantu.

Saya juga tidak percaya ada optimasi yang akan Anda lewatkan - kolom-kolom tersebut saat ini tidak diindeks, jadi tidak seperti optimizer dapat memilih metode akses yang berbeda untuk menentukan, katakanlah, semua balance_amountnilai> 10000. Ada mungkin situasi di mana jika Anda membuat indeks non-cluster di salah satu kolom Anda mungkin mendapatkan perkiraan sedikit lebih baik daripada jika indeks tidak ada, tetapi ini tidak ada hubungannya dengan nullability.

Saya tidak akan terlalu khawatir tentang ini dari perspektif kinerja. Saya kembali dan melihat sekelompok tampilan indeks yang telah saya buat selama bertahun-tahun dan kolom agregasi ini semuanya dapat dibatalkan. Mereka melakukan dengan baik.

Sejauh pemetaan objek berjalan, sekali lagi, saya tidak akan terlalu khawatir tentang hal itu. Karena aplikasi tidak dapat memperbarui tampilan yang diindeks, tidak masalah jika menurutnya itu balance_amountbisa null. Itu tidak akan pernah menerima null, dan tidak dapat mencoba untuk menulis null, jadi <shrug>.

Aaron Bertrand
sumber
@ Harun, tentang pemetaan objek: Saya menganggapnya layak untuk dilihat, karena seorang mapper kemungkinan akan menghasilkan objek yang tidak berguna / menyesatkan dengan tipe nullable yang tidak akan pernah benar-benar digunakan seperti itu.
Marcel