Чи є спосіб зробити нульову перевірку змінної в пункті WHERE лише один раз?


12

У мене запит на великій таблиці, який виглядає приблизно так:

declare @myIdParam int = 1

select * 
from myTable
where (@myIdParam is null or myTable.Id = @myIdParam)

Існує кілька подібних умов, як це у пункті де, і також є багато приєднань, але це короткий підсумок.

Ефективно, якщо @myIdParam недійсний, ми не хочемо обмежувати результати за допомогою цього параметра.

Я не DB Pro, але з моїх тестів, схоже, ця перевірка NULL робиться для кожного запису і не оптимізована жодним чином.

Якщо я видаляю нульову перевірку і припускаю, що параметр не є нульовим, запит миттєво повертається. В іншому випадку це займає до десяти секунд.

Чи є спосіб оптимізувати це, щоб перевірка робилася лише один раз під час виконання?


1
Подивіться на цю відповідь: stackoverflow.com/questions/3415582/… tl; dr useOPTION(RECOMPILE)
vercelli

@vercelli це робить свою справу. Зважаючи на те, що це питання дійсно стосується необов'язкових параметрів, я б сказав, що це дублікат того, який ви пов’язали.
Mystagogue

Напевно, але це повідомлення від 6 років тому. Можливо, для SqlServer 2014 або 2016 є новий підхід. (Я протестував це 2014 року без перекомпіляції і взяв назавжди)
vercelli

Оскільки ваш фактичний запит має багато необов’язкових параметрів, динамічний SQL забезпечить найкращу ефективність. Докладну статтю з цього питання див. У sommarskog.se/dyn-search.html .
Дан Гузман

@DanGuzman, що використовує "RECOMPILE", як викладено у запитанні vercelli. Я вважаю це найкращим варіантом для збалансування продуктивності та читабельності.
Mystagogue

Відповіді:


8

Один із способів полягає у використанні динамічного SQL, використовуючи нульову перевірку, щоб необов'язково додати ту частину пункту, де.

declare @myIdParam int = 1
declare @vc_dynamicsql varchar(max)

set @vc_dynamicsql = 'select * from myTable where 1=1'

if @myIdParam is not null
    set @vc_dynamicsql = @vc_dynamicsql + ' and  myTable.Id = @myIdParam'

EXECUTE sp_executesql @vc_dynamicsql

2
Я б волію не робити цього, але це рішення. Я сподіваюся, що хтось прийде разом зі значно кращим.
Містагога

1
Це найкращий спосіб обробляти цей клас пошукового запиту. StackOverflow відповідь посилається @vercelli містить великі посилання для того, як зробити це.
Макс Вернон

Це найкращий метод, але я помітив, що параметр @params для sp_ExecuteSQLвідсутній, а для @vc_dynamicsqlпараметра повинен бути a NVARCHAR.
Джеймс Андерсон

4

Щоразу, коли ви ставите функцію навколо стовпця "ISNULL (@var, table.col)", наприклад, ви видаляєте можливість SQL використовувати індекс. Це дійсно найкращий варіант, якщо ви хочете зберегти його в одному запиті.

@var IS NULL or @var = table.col

Інакше у вас є два варіанти. Перший - це динамічний SQL, і відповіді @ Mystagogue достатньо, щоб інакше ви могли поставити два запити на кшталт цього:

IF @var is NULL
     SELECT * FROM table
ELSE
     SELECT * FROM table WHERE @var = col

І в цьому форматі, і в динамічному SQL ви фактично отримаєте інший план запитів для кожного із запитів (що потенційно дасть кращу ефективність).


Sql у запитанні не використовує ISNULL або будь-яку іншу функцію.
Mystagogue

@MystagogueI Я посилався на видалену відповідь.
Кеннет Фішер

0

Що ж, ви можете:

declare @myIdParam int = 1;

select *
from myTable
where nullif(@myIdParam, myTable.Id) is null;

Однак майте на увазі, що nullif()функція по суті є обгорткою case. Це не срібна куля, яка магічно усуває ORі тим самим прискорює запит.


використання функцій в пункті, де стаття негативно впливає на продуктивність, оскільки перешкоджає використанню індексів (або я так чув)
Mystagogue

@ Містагога, так - зазвичай це робить умови пошуку не SARGable. На жаль, це єдиний спосіб, коли я знаю, як відповісти на ваше запитання, не вдаючись до динамічного SQL або декількох UNIONs. Коли у мене було таке точне завдання, я вибрав динамічний SQL.
Роджер Вольф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.