Come posso aiutare SQL Server a riconoscere la colonna della mia vista indicizzata NON NULL-grado?


9

Ho la seguente vista indicizzata definita in SQL Server 2008 (è possibile scaricare uno schema funzionante da gist a scopo di test):

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_ide transaction_amountsono tutti definiti come NOT NULLcolonne in dbo.transactions. Tuttavia, quando guardo la definizione della vista in Esplora oggetti di Management Studio, segna entrambe le colonne come balance_amounte -able nella vista.transaction_countNULL

Ho dato un'occhiata a diverse discussioni, questa è la più pertinente di esse, che suggerisce che alcune mescolanze di funzioni potrebbero aiutare SQL Server a riconoscere che una colonna di visualizzazione è sempre NOT NULL. Nel mio caso, tuttavia, non è possibile effettuare tale shuffle, poiché le espressioni sulle funzioni aggregate (ad esempio un ISNULL()over the SUM()) non sono consentite nelle viste indicizzate.

  1. C'è un modo posso aiutare SQL Server riconoscere che balance_amounte transaction_countsono NOT NULL-abile?

  2. In caso contrario, dovrei avere dubbi sul fatto che queste colonne vengano erroneamente identificate come NULL-able?

    Le due preoccupazioni che mi vengono in mente sono:

    • Qualsiasi oggetto dell'applicazione mappato alla vista dei saldi sta ottenendo una definizione errata di un saldo.
    • In casi molto limitati, alcune ottimizzazioni non sono disponibili per lo Strumento per ottimizzare le query poiché non ha una garanzia dal punto di vista della presenza di queste due colonne NOT NULL.

    Una di queste preoccupazioni è un grosso problema? Ci sono altre preoccupazioni che dovrei tenere a mente?


Sì, ci sono preoccupazioni, ad esempio il tuo ORM creerà tipi nullable, che a loro volta avranno bisogno di ulteriore attenzione nel codice quando li usi, il che è inutile (o addirittura fuorviante) nel tuo caso.
Marcel,

Questo sembra anche essere un problema in un cte ricorsivo quando si ricorre su un campo non nullable (nessun aggregato) sebbene un IsNull (..., 0) alla fine possa curare.
crokusek,

Risposte:


10

user_id, currency_ide transaction_amountsono tutti definiti come NOT NULLcolonne indbo.transactions

Mi sembra che SQL Server abbia una supposizione generale che un aggregato possa produrre un nullanche se i campi su cui opera lo sono not null. Questo è ovviamente vero in alcuni casi:

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

Ed è anche vero nelle versioni generalizzate di group bylikecube

Questo caso di test più semplice illustra il punto in cui qualsiasi aggregato viene interpretato come 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 questa è una limitazione (anche se minore) di SQL Server - alcuni altri RDBMS consentono la creazione di determinati vincoli su viste che non vengono applicate ed esistono solo per fornire indizi all'ottimizzatore, anche se penso che l'unicità sia più probabile aiutare a generare un buon piano di query rispetto a "nullability"


Se la nullità della colonna è importante, forse per l'uso con un ORM, considera la possibilità di racchiudere la vista indicizzata in un'altra vista che garantisca semplicemente la non annullabilità utilizzando 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;

Dettagli di Esplora oggetti SSMS


5

Non credo ci sia modo di forzare SQL Server a riconoscere queste colonne come non annullabili, anche se chiaramente non lo sono. Puoi provare a cambiare l'ordine di definizione ISNULL/ COALESCEattorno all'espressione all'interno SUM() , ad esempio, ma non sarà di aiuto.

Inoltre, non credo che ci siano delle ottimizzazioni che ti perderai - quelle colonne non sono attualmente indicizzate, quindi non è come se l'ottimizzatore potesse scegliere un metodo di accesso diverso per determinare, diciamo, tutti i balance_amountvalori> 10000. potrebbe essere una situazione in cui se si crea un indice non cluster su una di quelle colonne si potrebbero ottenere stime leggermente migliori rispetto a se l'indice non fosse presente, ma ciò non ha nulla a che fare con nullabilità.

Non sarei troppo preoccupato per questo dal punto di vista delle prestazioni. Sono tornato indietro e ho guardato un sacco di viste indicizzate che ho creato nel corso degli anni e queste colonne di aggregazione sono tutte nulle. Si esibiscono bene.

Per quanto riguarda la mappatura degli oggetti, di nuovo, non sarei troppo preoccupato per questo. Poiché l'applicazione non può aggiornare la vista indicizzata, non importa se pensa che balance_amountpossa essere null. Non riceverà mai un null, e non può provare a scrivere un null, quindi <shrug>.



@Aaron, sulla mappatura degli oggetti: ritengo che valga la pena guardarlo, dal momento che un mapper probabilmente genererà oggetti inutili / fuorvianti con tipi nullable che non saranno mai realmente usati come tali.
Marcel,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.