impedire l'operatore di inserimento indice cluster in vista indicizzata non qualificata


8

Qualcuno conosce una soluzione alternativa per questo? In sostanza, la procedura memorizzata forza un operatore di inserimento contro la vista indicizzata, anche se le righe non sono idonee. Di conseguenza, si è verificato un errore di trasmissione. Tuttavia, per ad hocs, sql elimina correttamente la vista dalla considerazione.

Considera il seguente schema:

create table testdata (
    testid int identity(1,1) primary key
  , kind varchar(50)
  , data nvarchar(4000))
go
create view integer_testdata with schemabinding
as
select cast(a.data as int) data, a.kind, a.testid
  from dbo.testdata a
 where a.kind = 'integer'
go
create unique clustered index cl_intdata on integer_testdata(data)
go
create procedure insert_testdata
(
    @kind varchar(50)
  , @data nvarchar(4000)
)
as
begin
  insert into testdata (kind, data) values (@kind, @data)
end
go

Questi funzionano tutti:

insert into testdata (kind, data) values ('integer', '1234');
insert into testdata (kind, data) values ('integer', 12345);
insert into testdata (kind, data) values ('noninteger', 'noninteger');
exec insert_testdata @kind = 'integer', @data = '123456';
exec insert_testdata @kind = 'integer', @data = 1234567;

Questo fallisce:

exec insert_testdata @kind = 'noninteger', @data = 'noninteger';

Un confronto tra i "piani di esecuzione stimati":

insert into testdata (kind, data) values ('noninteger', 'noninteger'): inserisci qui la descrizione dell'immagine

exec insert_testdata @kind = 'noninteger', @data = 'noninteger': inserisci qui la descrizione dell'immagine


Qualche differenza notevole tra il piano proc memorizzato nella cache ad hoc e quello memorizzato nella cache per caso?
Ali Razeghi,

sì, quando si esegue ad hoc, non si ottiene alcun operatore rispetto alla vista indicizzata ... Penso che sql sia abbastanza intelligente da vedere c'è un filtro sulla vista ed eliminarlo dalla considerazione (questa eliminazione non si verifica nella proc)
cocogorilla,

4
Non sei in grado di testare ma aggiungere option (recompile)aiuto?
Martin Smith,

2
Solo per curiosità, quale problema stai cercando di risolvere. Questo puzza di un problema XY .
Max Vernon,

1
@MaxVernon Sto lavorando con una struttura di dati esistente e ho bisogno di una rapida ricerca di valori interi univoci memorizzati nel sottoinsieme di nvarchar (4000), un filtro su un'altra colonna definisce quel sottoinsieme di righe.
Cocogorilla,

Risposte:


6

Grazie per aver fornito uno script completo per ricreare il problema.

L'ho provato con SQL Server 2014 Express.

Quando aggiungo OPTION(RECOMPILE)funziona:

ALTER procedure [dbo].[insert_testdata]
(
    @kind varchar(50)
  , @data nvarchar(4000)
)
as
begin
  insert into testdata (kind, data) 
  values (@kind, @data)
  OPTION(RECOMPILE);
end

Quando eseguo questo in SSMS:

exec insert_testdata @kind = 'noninteger', @data = 'noninteger';

Ricevo questo messaggio:

(1 row(s) affected)

e una riga viene aggiunta alla tabella.

Quale versione di SQL Server stai usando? Ricordo vagamente che nelle versioni precedenti al 2008 ciò si OPTION(RECOMPILE)comportava in modo leggermente diverso.


Sto lavorando con una struttura di dati esistente e ho bisogno di una rapida ricerca di valori interi univoci memorizzati nel sottoinsieme di nvarchar (4000), un filtro su un'altra colonna definisce quel sottoinsieme di righe.

In questo caso potrebbe essere meglio utilizzare l' indice filtrato anziché la vista indicizzata:

CREATE UNIQUE NONCLUSTERED INDEX [IX_DataFiltered] ON [dbo].[testdata]
(
    [data] ASC
)
WHERE ([kind]='integer')

L'ottimizzatore dovrebbe utilizzare questo indice quando il WHEREfiltro della query corrisponde esattamente alla WHEREclausola dell'indice.

Sì, qui l'indice è sulla nvarcharcolonna che potrebbe non essere la cosa migliore, specialmente se si unisce questa tabella con una intcolonna di un'altra tabella o si tenta di filtrare i valori in questa colonna utilizzando i intvalori.


Un'altra variante che viene in mente è persistente colonna calcolata che i convertiti nvarchara int. In sostanza è molto simile alla tua vista, ma i nvarcharvalori persistenti che vengono convertiti intvengono memorizzati con la stessa tabella, non in un oggetto separato.

CREATE TABLE [dbo].[testdata](
    [testid] [int] IDENTITY(1,1) NOT NULL,
    [kind] [varchar](50) NULL,
    [data] [nvarchar](4000) NULL,
    [int_data]  AS (case when [kind]='integer' then CONVERT([int],[data]) end) PERSISTED,
PRIMARY KEY CLUSTERED 
(
    [testid] ASC
))


CREATE UNIQUE NONCLUSTERED INDEX [IX_int_data_filtered] ON [dbo].[testdata]
(
    [int_data] ASC
)
WHERE ([kind]='integer')

Con questa configurazione ho cercato di utilizzare la procedura memorizzata originale per inserire le righe e ha funzionato anche senza OPTION(RECOMPILE).


In realtà, sembra che il motivo principale per cui funziona la colonna persistente sopra è che io uso CASE. Se aggiungo CASEalla definizione della tua vista, la procedura memorizzata funziona senza OPTION(RECOMPILE).

create view integer_testdata2 with schemabinding
as
select 
    case when a.kind='integer' then CONVERT(int, a.data) end as data
    , a.kind, a.testid
from dbo.testdata a
where a.kind = 'integer'
go

Non sono sicuro che l'indice filtrato funzionerà bene perché la larghezza della colonna è 4000 (ben al di sopra del limite di 900). Non avevo pensato di utilizzare una ricompilazione dell'opzione suggerimento query ... Stavo applicando la ricompilazione per l'intera procedura. Il tuo suggerimento funziona per tutti i miei casi di test! Grazie.
cocogorilla,

1
Sì, l'indice filtrato sulla colonna originale potrebbe non essere molto utile. Ho aggiunto un'altra variante con colonna calcolata persistente.
Vladimir Baranov,

Adoro l'opzione della colonna calcolata persistente ... che mi
sembra la
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.