Jak mogę pomóc programowi SQL Server rozpoznać, że kolumna zindeksowanego widoku NIE jest w stanie NULL?

9

Mam następujący widok indeksowany zdefiniowany w SQL Server 2008 (możesz pobrać działający schemat z gist do celów testowych):

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_idi transaction_amountwszystkie są zdefiniowane jako NOT NULLkolumny w dbo.transactions. Jednak kiedy patrzę na definicji widoku w zarządzaniu Studio Object Explorer, to znaki obu balance_amounti transaction_countjak NULL-able kolumn w widoku.

Rzuciłem okiem na kilka dyskusji, z których ta jest najbardziej odpowiednia, sugerując, że pewne tasowanie funkcji może pomóc SQL Serverowi rozpoznać, że kolumna widoku jest zawsze NOT NULL. W moim przypadku takie tasowanie nie jest możliwe, ponieważ wyrażenia dotyczące funkcji agregujących (np. ISNULL()Ponad SUM()) niedozwolone w widokach indeksowanych.

  1. Czy jest jakiś sposób, w jaki mogę pomóc SQL Serverowi to rozpoznać balance_amounti czy transaction_countmożna NOT NULL?

  2. Jeśli nie, to czy powinienem mieć jakiekolwiek obawy, że te kolumny zostaną omyłkowo zidentyfikowane jako możliwe NULL?

    Dwa problemy, o których mogłem pomyśleć, to:

    • Wszelkie obiekty aplikacji odwzorowane na widok sald otrzymują niepoprawną definicję salda.
    • W bardzo ograniczonych przypadkach niektóre optymalizacje nie są dostępne dla Optymalizatora zapytań, ponieważ nie ma gwarancji z widoku, że te dwie kolumny są NOT NULL.

    Czy któraś z tych kwestii jest ważna? Czy są jeszcze jakieś obawy, o których powinienem pamiętać?

Nick Chammas
źródło
Tak, istnieją obawy, na przykład twoja ORM utworzy typy zerowalne, które z kolei będą wymagać dodatkowej uwagi w kodzie podczas ich używania, co jest bezużyteczne (lub nawet wprowadzające w błąd) w twoim przypadku.
Marcel
Wydaje się to również stanowić problem w rekurencyjnym cte podczas rekurencji w polu, które nie ma wartości zerowej (bez agregacji), chociaż IsNull (..., 0) na końcu może wyleczyć.
crokusek

Odpowiedzi:

10

user_id, currency_idi transaction_amountwszystkie są zdefiniowane jako NOT NULLkolumny wdbo.transactions

Wydaje mi się, że SQL Server ma ogólne założenie, że agregacja może wygenerować nullnawet, jeśli pola, na których operuje, są not null. Jest to oczywiście prawda w niektórych przypadkach:

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

Dotyczy to również uogólnionych wersji programu group bylikecube

Ten prostszy przypadek testowy pokazuje, że każdy agregat jest interpretowany jako zerowalny:

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

IMO jest to ograniczenie (choć niewielkie) SQL Server - niektóre inne RDBMS pozwalają na tworzenie pewnych ograniczeń w widokach, które nie są egzekwowane i istnieją tylko po to, aby dać wskazówki optymalizatorowi, chociaż myślę, że „wyjątkowość” jest bardziej prawdopodobna pomoc w generowaniu dobrego planu zapytań niż „zerowalność”


Jeśli zerowalność kolumny jest ważna, być może do użycia z ORM, rozważ zawinięcie indeksowanego widoku w innym widoku, który po prostu gwarantuje brak zerowania przy użyciu 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;

Szczegóły eksploratora obiektów SSMS

Jack mówi, że spróbuj topanswers.xyz
źródło
5

Nie sądzę, aby można było zmusić program SQL Server do rozpoznania tych kolumn jako zerowalne, nawet jeśli najwyraźniej nie są. Możesz na przykład spróbować zmienić kolejność definiowania ISNULL/ COALESCEotaczania wyrażenia w środku SUM() , ale to nie pomoże.

Nie wierzę też, że istnieją jakieś optymalizacje, których przegapisz - te kolumny nie są obecnie indeksowane, więc to nie jest tak, że optymalizator może wybrać inną metodę dostępu, aby określić, powiedzmy, wszystkie balance_amountwartości> 10000. Tam może się zdarzyć, że jeśli utworzysz indeks nieklastrowany w jednej z tych kolumn, możesz uzyskać nieco lepsze oszacowania niż w przypadku braku tego indeksu, ale nie ma to nic wspólnego z zerowalnością.

Nie martwiłbym się tym zbytnio z punktu widzenia wydajności. Wróciłem i spojrzałem na kilka indeksowanych widoków, które stworzyłem przez lata i wszystkie te kolumny agregacji są zerowalne. Występują dobrze.

Jeśli chodzi o mapowanie obiektów, znowu nie martwiłbym się tym. Ponieważ aplikacja nie może zaktualizować widoku indeksowanego, nie ma znaczenia, czy tak balance_amountjest null. Nigdy nie otrzyma nulli nie może spróbować napisać null, więc <shrug>.

Aaron Bertrand
źródło
@Aaron, o mapowaniu obiektów: Uważam, że warto przyjrzeć się temu, ponieważ program mapujący prawdopodobnie wygeneruje bezużyteczne / wprowadzające w błąd obiekty o typach zerowalnych, które nigdy tak naprawdę nie będą używane.
Marcel