Alexeytur
@Alexeytur

Почему выполнение подзапроса медленнее?

Почему выполнение запроса как подзапрос медленнее, чем его выполнение последовательно такое же количество раз?
select top 1 login from SiemensLicUsage 
  where CONVERT(DATE,SiemensLicUsage.logDateTime)=DATEADD(DAY,-1,CONVERT(DATE, GETDATE()))
    and SiemensLicUsage.licId='cam_base' and SiemensLicUsage.filename='109012981';

Запрос выполняется быстро.
Но если выполнить его как подзапрос для каждой даты из заданного диапазона дат:
DECLARE @StartDate DATE = DATEADD(DAY,-10,CONVERT(DATE,GETDATE()))
  , @EndDate DATE = CONVERT(DATE,GETDATE())
select dt,
(
	select top 1 login from SiemensLicUsage where SiemensLicUsage.licId='cam_base' and 
	SiemensLicUsage.filename='109012981' and CONVERT(DATE,SiemensLicUsage.logDateTime)=CONVERT(DATE, dt)
) LicUsageCount 
from
-- Генерация диапазона дат между заданными значениями------------------
(
	SELECT  DATEADD(DAY, nbr - 1, @StartDate) dt	
	FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY c.object_id ) AS nbr
			  FROM      sys.columns c
			) nbrs
	WHERE   nbr - 1 <= DATEDIFF(DAY, @StartDate, @EndDate)
) dates
------------------------------------------------------------------------
order by dt desc;

Выполняется 5 секунд, хотя в диапазоне всего 11 дат.
Пробую этот подзапрос выполнить последовательно для тех же 11 дат:
spoiler

select top 1 login from SiemensLicUsage where CONVERT(DATE,SiemensLicUsage.logDateTime)=CONVERT(DATE, GETDATE())
and SiemensLicUsage.licId='cam_base' and SiemensLicUsage.filename='109012981';  
select top 1 login from SiemensLicUsage where CONVERT(DATE,SiemensLicUsage.logDateTime)=DATEADD(DAY,-1,CONVERT(DATE, GETDATE()))
and SiemensLicUsage.licId='cam_base' and SiemensLicUsage.filename='109012981';
select top 1 login from SiemensLicUsage where CONVERT(DATE,SiemensLicUsage.logDateTime)=DATEADD(DAY,-2,CONVERT(DATE, GETDATE()))
and SiemensLicUsage.licId='cam_base' and SiemensLicUsage.filename='109012981';
select top 1 login from SiemensLicUsage where CONVERT(DATE,SiemensLicUsage.logDateTime)=DATEADD(DAY,-3,CONVERT(DATE, GETDATE()))
and SiemensLicUsage.licId='cam_base' and SiemensLicUsage.filename='109012981';
select top 1 login from SiemensLicUsage where CONVERT(DATE,SiemensLicUsage.logDateTime)=DATEADD(DAY,-4,CONVERT(DATE, GETDATE()))
and SiemensLicUsage.licId='cam_base' and SiemensLicUsage.filename='109012981';
select top 1 login from SiemensLicUsage where CONVERT(DATE,SiemensLicUsage.logDateTime)=DATEADD(DAY,-5,CONVERT(DATE, GETDATE()))
and SiemensLicUsage.licId='cam_base' and SiemensLicUsage.filename='109012981';
select top 1 login from SiemensLicUsage where CONVERT(DATE,SiemensLicUsage.logDateTime)=DATEADD(DAY,-6,CONVERT(DATE, GETDATE()))
and SiemensLicUsage.licId='cam_base' and SiemensLicUsage.filename='109012981';
select top 1 login from SiemensLicUsage where CONVERT(DATE,SiemensLicUsage.logDateTime)=DATEADD(DAY,-7,CONVERT(DATE, GETDATE()))
and SiemensLicUsage.licId='cam_base' and SiemensLicUsage.filename='109012981';
select top 1 login from SiemensLicUsage where CONVERT(DATE,SiemensLicUsage.logDateTime)=DATEADD(DAY,-8,CONVERT(DATE, GETDATE()))
and SiemensLicUsage.licId='cam_base' and SiemensLicUsage.filename='109012981';
select top 1 login from SiemensLicUsage where CONVERT(DATE,SiemensLicUsage.logDateTime)=DATEADD(DAY,-9,CONVERT(DATE, GETDATE()))
and SiemensLicUsage.licId='cam_base' and SiemensLicUsage.filename='109012981';
select top 1 login from SiemensLicUsage where CONVERT(DATE,SiemensLicUsage.logDateTime)=DATEADD(DAY,-10,CONVERT(DATE, GETDATE()))
and SiemensLicUsage.licId='cam_base' and SiemensLicUsage.filename='109012981';


Выполняется 2 с копейками секунды.
Массив дат в будущем может быть очень большим.
Генерация массива дат без подзапроса выполняется также быстро.
Генерацию массива дат пробовал выносить в CTE и во временную таблицу, результат тот же.
Версия SQL Server 2008 R2.
  • Вопрос задан
  • 117 просмотров
Решения вопроса 1
tsklab
@tsklab Куратор тега SQL Server
Здесь отвечаю на вопросы.
Конечно нужно смотреть предполагаемый и действительный план выполнения запроса.
Но попробуйте добавить ORDER BY по кластерному индексу. Или есть индекс по login?

Без подзапросов и SELECT TOP
DECLARE @StartDate DATE = DATEADD(DAY,-10,CONVERT(DATE,GETDATE())), 
        @EndDate DATE = CONVERT(DATE,GETDATE())
SELECT CONVERT(DATE, logDateTime), [login]
  FROM SiemensLicUsage
  WHERE (licId = 'cam_base') AND ([filename] = '109012981')
      AND CONVERT(DATE, logDateTime) BETWEEN @StartDate AND @EndDate
  GROUP BY CONVERT(DATE, logDateTime), [login]
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы