Чому помилка запиту з порожнім результатом задається в SQL Server 2012?


31

Під час виконання наступних запитів у MS SQL Server 2012 другий запит не вдається, але не перший. Крім того, якщо запускати без пунктів де обидва запити вийдуть з ладу. Я втрачаю, чому або не вдасться, оскільки обоє повинні мати порожні результати. Будь-яка допомога / розуміння цінується.

create table #temp
(id     int primary key)

create table #temp2
(id     int)

select 1/0
from #temp
where id = 1

select 1/0
from #temp2
where id = 1

Відповіді:


39

Початковий погляд на плани виконання показує, що вираз 1/0визначений в операторах Compute Scalar:

Графічні плани

Тепер, навіть незважаючи на те, що плани виконання дій починають виконуватися в крайній лівій частині, ітеративно виклику Openі GetRowметодів дочірніх ітераторів для повернення результатів, SQL Server 2005 і пізніше містить оптимізацію, згідно з якою вирази часто визначаються лише Обчислювальним скаляром, а оцінка відкладається до наступного Операція вимагає результату :

Обчислювальні скалярні оператори, які відображаються в Showplans, створених SET STATISTICS XML, можуть не містити елемента RunTimeInformation.  У графічних вітринах відображення, фактичні рядки, фактичні перемотування та фактичні перемотування назад можуть бути відсутні у вікні властивостей, якщо в студії управління SQL Server вибрано параметр Включити фактичний план виконання.  Коли це відбувається, це означає, що хоча ці оператори використовувались у складеному плані запитів, їх роботу виконували інші оператори в плані запитів виконання.  Також зауважте, що кількість виконань у виводі Showplan, що генерується SET STATISTICS PROFILE, еквівалентна сумі відмовлень і перемотування в Showplans, згенерованих SET STATISTICS XML.  Від: Книги MSDN Online

У цьому випадку результат вираження потрібен лише під час збирання рядка для повернення до клієнта (що можна подумати про те, що відбувається у зеленому SELECTзначку). За цією логікою відкладене оцінювання означало б, що вираз ніколи не оцінюється, оскільки жоден план не створює ряд повернення. Щоб трохи попрацювати над точкою, ні кластерний пошук пошуку, ні сканування таблиці не повертають рядка, тому немає рядка для збирання для повернення до клієнта.

Однак існує окрема оптимізація, згідно з якою деякі вирази можуть бути ідентифіковані як константи часу виконання і так оцінені один раз перед початком виконання запиту . У цьому випадку вказівку на цю ситуацію можна знайти в XML-шоу XML (План пошуку кластерних індексів зліва, План сканування таблиці справа):

Showplan XML

Про основні механізми та про те, як вони можуть вплинути на ефективність, я писав у цій публікації в блозі . Використовуючи інформацію, надану там, ми можемо змінити перший запит, щоб обидва вирази були оцінені та кешовані перед початком виконання:

select 1/0 * CONVERT(integer, @@DBTS)
from #temp
where id = 1

select 1/0
from #temp2
where id = 1

Тепер перший план також містить посилання на постійний вираз, і обидва запити створюють повідомлення про помилку. XML для першого запиту містить:

Постійна експресія

Додаткова інформація: Обчислити скаляри, вирази та продуктивність


21

Я збираюся розумно здогадатися (і, напевно, залучатимуть гуру SQL Server, який міг би дати дійсно детальну відповідь).

Перший запит підходить до виконання як:

  1. Скануйте індекс первинного ключа
  2. Знайдіть значення в таблиці даних, необхідні для запиту

Він обирає цей шлях, оскільки у вас є whereпункт про первинний ключ. Він ніколи не потрапляє на другий крок, тому запит не виходить з ладу.

Другий не має первинного ключа, на якому він повинен працювати, тому він звертається до запиту як:

  1. Зробити повну таблицю сканування даних та отримати необхідні значення

Одне з цих значень 1/0викликає проблему.

Це приклад оптимізації запиту SQL Server. Здебільшого це гарна річ. SQL Server перемістить умови з selectоперації сканування таблиці. Це часто економить кроки в оцінці запиту.

Але ця оптимізація не є непомітною справою. Насправді це, здається, порушує саму документацію на SQL Server , що говорить про те, що whereстаття оцінюється до початку select. Що ж, вони можуть мати якесь ерудитне пояснення, що це означає. Для більшості людей, однак, логічно обробляючи whereраніше, це selectозначатиме (серед іншого) "не створювати selectпомилки -клаузи на рядках, не повернених користувачеві".


1
+1 не майте поняття, якщо ви праві, але найкраща відповідь, яку я бачу, враховуючи єдину різницю - це первинний ключ.

1
@GordonLinoff Пол Рандал щойно підтвердив через Twitter, що ваша відповідь лунає.
SchmitzIT

4
@Still, фактичний порядок виконання, який би різний не повинен призводити до подібних повідомлень про помилки.
ypercubeᵀᴹ

7
@ypercube Ерланд Соммарський погодився б із вами (елемент Connect)
Пол Білий каже GoFundMonica

2
Дякую за вказівник - я ввійшов у систему та відхилив запит.
Гордон Лінофф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.