¿Cómo puedo ayudar a SQL Server a reconocer que mi columna de vista indexada NO es NULL?

9

Tengo la siguiente vista indizada definida en SQL Server 2008 (puede descargar un esquema de trabajo de GIST para fines de prueba):

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_idy transaction_amountestán todos definidos como NOT NULLcolumnas en dbo.transactions. Sin embargo, cuando miro a la definición de la vista en el objeto de estudio de la gerencia Explorer, ambas marcas balance_amounty transaction_countcomo NULLcolumnas -able en la vista.

He echado un vistazo a varias discusiones, éste es el más relevante de ellos, que sugieren cierta redistribución de las funciones puede ayudar a SQL Server reconoce que una columna de vista es siempre NOT NULL. Sin embargo, en este caso no es posible mezclar, ya que las expresiones en funciones agregadas (por ejemplo, un ISNULL()sobre SUM()) no están permitidas en las vistas indizadas.

  1. ¿Hay alguna manera de que pueda ayudar a SQL Server reconoce que balance_amounty transaction_countson NOT NULL-able?

  2. Si no es así, ¿debo preocuparme de que estas columnas se identifiquen erróneamente como NULL-able?

    Las dos preocupaciones que se me ocurren son:

    • Los objetos de aplicación asignados a la vista de saldos obtienen una definición incorrecta de un saldo.
    • En casos muy limitados, ciertas optimizaciones no están disponibles para el Optimizador de consultas, ya que no tiene una garantía desde el punto de vista de que estas dos columnas lo estén NOT NULL.

    ¿Alguna de estas preocupaciones es un gran problema? ¿Hay alguna otra preocupación que deba tener en cuenta?

Nick Chammas
fuente
Sí, existen preocupaciones, por ejemplo, su ORM creará tipos anulables, que a su vez necesitarán un cuidado especial en el código cuando los use, lo cual es inútil (o incluso engañoso) en su caso.
Marcel
Esto también parece ser un problema en un cte recursivo cuando se recurre en un campo no anulable (sin agregado) aunque un IsNull (..., 0) al final puede curar.
crokusek

Respuestas:

10

user_id, currency_idy transaction_amountestán todos definidos como NOT NULLcolumnas endbo.transactions

Me parece que SQL Server tiene una suposición general de que un agregado puede producir nullincluso si los campos en los que opera son not null. Esto es obviamente cierto en ciertos casos:

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

Y también es cierto en las versiones generalizadas de group bylikecube

Este caso de prueba más simple ilustra el punto de que cualquier agregado se interpreta como anulable:

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

En mi opinión, esta es una limitación (aunque menor) de SQL Server: algunos otros RDBMS permiten la creación de ciertas restricciones en las vistas que no se aplican y existen solo para dar pistas al optimizador, aunque creo que es más probable que la 'singularidad' ayuda para generar un buen plan de consulta que 'nulabilidad'


Si la nulabilidad de la columna es importante, tal vez para usar con un ORM, considere ajustar la vista indizada en otra vista que simplemente garantice la no nulabilidad mediante 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;

Detalles del Explorador de objetos SSMS

Jack dice que intente topanswers.xyz
fuente
5

No creo que pueda forzar a SQL Server a reconocer estas columnas como no anulables, aunque claramente no lo son. Puede intentar cambiar el orden de cómo define ISNULL/ COALESCEalrededor de la expresión dentro SUM() , por ejemplo, pero no va a ayudar.

Tampoco creo que haya optimizaciones que pueda perderse: esas columnas no están indexadas actualmente, por lo que no es que el optimizador pueda elegir un método de acceso diferente para determinar, por ejemplo, todos los balance_amountvalores> 10000. Hay puede ser una situación en la que si crea un índice no agrupado en una de esas columnas puede obtener estimaciones ligeramente mejores que si el índice no está allí, pero esto no tiene nada que ver con la nulabilidad.

No estaría demasiado preocupado por esto desde una perspectiva de rendimiento. Regresé y miré un montón de vistas indexadas que he creado a lo largo de los años y estas columnas de agregación son anulables. Se desempeñan muy bien.

En cuanto al mapeo de objetos, una vez más, no me preocuparía demasiado. Dado que la aplicación no puede actualizar la vista indizada, no importa si cree que balance_amountpuede ser así null. Nunca va a recibir un null, y no puede intentar escribir un null, entonces <shrug>.

Aaron Bertrand
fuente
@ Aaron, sobre el mapeo de objetos: considero que vale la pena mirarlo, ya que un mapeador probablemente generará objetos inútiles / engañosos con tipos anulables que nunca se usarán realmente como tales.
Marcel