Verhindern des Operators zum Einfügen von Clustered-Indizes in einer nicht qualifizierten indizierten Ansicht

8

Kennt jemand eine Problemumgehung dafür? Im Wesentlichen erzwingt die gespeicherte Prozedur einen Einfügeoperator gegen die indizierte Ansicht, obwohl die Zeilen nicht qualifiziert sind. Infolgedessen liegt ein Umwandlungsfehler vor. Bei Ad-hocs entfernt SQL die Ansicht jedoch korrekt aus der Betrachtung.

Betrachten Sie das folgende 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

Diese alle funktionieren:

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;

Dies schlägt fehl:

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

Ein Vergleich der "geschätzten Ausführungspläne":

insert into testdata (kind, data) values ('noninteger', 'noninteger'):: Geben Sie hier die Bildbeschreibung ein

exec insert_testdata @kind = 'noninteger', @data = 'noninteger':: Geben Sie hier die Bildbeschreibung ein

Cocogorilla
quelle
Gibt es bemerkenswerte Unterschiede zwischen dem Ad-hoc- und dem zwischengespeicherten gespeicherten Proc-Plan pro Chance?
Ali Razeghi
Ja, wenn Sie Ad-hoc ausführen, erhalten Sie keine Operatoren für die indizierte Ansicht. Ich denke, SQL ist intelligent genug, um zu erkennen, dass die Ansicht einen Filter enthält, und ihn aus der Betrachtung zu entfernen (diese Eliminierung findet in der nicht statt proc)
cocogorilla
4
Nicht in der Lage zu testen, aber hilft das Hinzufügen option (recompile)?
Martin Smith
2
Nur aus Neugier, welches Problem versuchen Sie zu lösen. Das riecht nach einem XY-Problem .
Max Vernon
1
@MaxVernon Ich arbeite mit einer vorhandenen Datenstruktur und benötige eine schnelle Suche nach eindeutigen Ganzzahlwerten, die in einer Teilmenge von nvarchar (4000) gespeichert sind. Ein Filter in einer anderen Spalte definiert diese Teilmenge von Zeilen.
Cocogorilla

Antworten:

6

Vielen Dank, dass Sie ein vollständiges Skript zur Neuerstellung des Problems bereitgestellt haben.

Ich habe es mit SQL Server 2014 Express getestet.

Wenn ich es hinzufüge OPTION(RECOMPILE), funktioniert es:

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

Wenn ich dies in SSMS ausführe:

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

Ich bekomme diese Nachricht:

(1 row(s) affected)

und eine Zeile wird der Tabelle hinzugefügt.

Welche Version von SQL Server verwenden Sie? Ich erinnere mich vage, dass sich dies in Versionen vor 2008 OPTION(RECOMPILE)etwas anders verhalten hat.


Ich arbeite mit einer vorhandenen Datenstruktur und benötige eine schnelle Suche nach eindeutigen Ganzzahlwerten, die in einer Teilmenge von nvarchar (4000) gespeichert sind. Ein Filter in einer anderen Spalte definiert diese Teilmenge von Zeilen.

In diesem Fall ist es möglicherweise besser, einen gefilterten Index anstelle einer indizierten Ansicht zu verwenden:

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

Das Optimierungsprogramm sollte diesen Index verwenden, wenn der WHEREFilter der Abfrage genau mit der WHEREKlausel des Index übereinstimmt .

Ja, hier befindet sich der Index in einer nvarcharSpalte, was möglicherweise nicht das Beste ist, insbesondere wenn Sie diese Tabelle mit einer intSpalte einer anderen Tabelle verknüpfen oder versuchen, Werte in dieser Spalte mithilfe von intWerten zu filtern .


Eine weitere Variante, die den Sinn kommt , ist beharrte berechnete Spalte , dass Konvertiten nvarcharzu int. Im Wesentlichen ist es Ihrer Ansicht sehr ähnlich, aber die persistierten nvarcharWerte, in die konvertiert intwird, werden mit derselben Tabelle und nicht in einem separaten Objekt gespeichert.

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')

Mit dieser Einstellung habe ich versucht, Ihre ursprünglich gespeicherte Prozedur zum Einfügen von Zeilen zu verwenden, und es hat auch ohne funktioniert OPTION(RECOMPILE).


Tatsächlich scheint es, dass der Hauptgrund, warum die oben genannte Spalte funktioniert, darin besteht, dass ich sie verwende CASE. Wenn ich CASEzur Definition Ihrer Ansicht hinzufüge , funktioniert die gespeicherte Prozedur ohne 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
Vladimir Baranov
quelle
Ich bin nicht sicher, ob der gefilterte Index gut funktioniert, da die Spaltenbreite 4000 beträgt (deutlich über dem Grenzwert von 900). Ich hatte nicht daran gedacht, eine Option zum Neukompilieren von Abfragehinweisen zu verwenden ... Ich habe die Neukompilierung auf die gesamte Prozedur angewendet. Ihr Vorschlag funktioniert für alle meine Testfälle! Vielen Dank.
Cocogorilla
1
Ja, der gefilterte Index für die ursprüngliche Spalte ist möglicherweise nicht sehr nützlich. Ich habe eine weitere Variante mit persistierter berechneter Spalte hinzugefügt.
Vladimir Baranov
Ich liebe die Option für persistierte berechnete Spalten ... die mir als die richtige Lösung erscheint
Cocogorilla