Проблема с MS SQL 2012 после перехода SQL 2005
От: gwg-605 Россия  
Дата: 14.05.15 15:34
Оценка:
Всем привет!

Вылезла неожиданно проблема с MS SQL 2012 после перехода с MS SQL 2005. Запрос стал выполняться 40-50 сукунд вместо нескольких милисекунд.
select a.dg dt, dt0.value_int v0, dt1.value_int v1    
from DeviceTimesByMinutes a 
    left outer join DeviceValues dt0 
        on a.dg = dbo.fn_RoundToMinutes( dt0.recorded ) 
    left outer join DeviceValues dt1 
        on a.dg = dbo.fn_RoundToMinutes( dt1.recorded )    
where 
    a.dg >= '2015-05-09 05:07:52' 
    and a.dg < '2015-05-12 05:07:52' 
    and dt0.nodeid = 'house0' 
    and dt0.deviceid = 'therm0' 
    and dt0.recorded >= '2015-05-09 05:07:52' 
    and dt0.recorded < '2015-05-12 05:07:52' 
    and dt1.nodeid = 'house0' 
    and dt1.deviceid = 'therm1' 
    and dt1.recorded >= '2015-05-09 05:07:52' 
    and dt1.recorded < '2015-05-12 05:07:52'
order by dt
GO

CREATE VIEW [dbo].[DeviceTimesByMinutes]
AS
select dbo.fn_RoundToMinutes( recorded ) as dg
from DeviceValues
group by dbo.fn_RoundToMinutes( recorded )
GO

CREATE FUNCTION [dbo].[fn_RoundToMinutes]( @date AS DATETIME )
RETURNS DATETIME
AS
BEGIN
  DECLARE @ret AS DATETIME;
  SET @ret = DATEADD( ss, -DATEPART( ss, @date ), @date );
  RETURN @ret;
END
GO

Изначально запрос был без вот этих строк:
    and dt0.recorded >= '2015-05-09 05:07:52' 
    and dt0.recorded < '2015-05-12 05:07:52' 
    and dt1.recorded >= '2015-05-09 05:07:52' 
    and dt1.recorded < '2015-05-12 05:07:52'

Запустил и офигел:
Две Table Spool операции расщиперивают данные почти до 19 миллионов строк

Добавил дополнительные условия по времени для каждого join-a, вроде проблема уменьшилась работало около 5-6 секунд, не решение — но не так проблемно. Через какое-то время добавилось записей в таблицу и проблема возобновилась. Сейчас в таблице 134000 строк.

Запускал аналайзер, он сказал что ничего не может соптимизировать.

Куда копать. Я реально сиквелом уже лет пять не знимался.
Re: Проблема с MS SQL 2012 после перехода SQL 2005
От: Tigor Россия  
Дата: 14.05.15 18:24
Оценка: 2 (1) +1
Советую задуматься над смыслом всех этих манипуляций.
К этому запросу есть несколько больших вопросов.

1. left join
Лефт джойн означает: "хочу, чтобы строка из первой таблицы попала в результат, даже если не удалось найти строку во второй"
И это работает, пока в where нету чего-нить подобного dt0.nodeid = 'house0'
Как только написано такое, лефт джойн превращается в inner join.
Возникают сомнения, понимал ли автор, что именно ему нужно.
Если нужен лефт джойн, то необходимо либо
a) дополнить where: dt0.nodeid = 'house0' or dt0.nodeid is null
b) (предпочтительнее) перенести фильтр dt0.nodeid = 'house0' в left join
       left outer join DeviceValues dt0 
         on a.dg = dbo.fn_RoundToMinutes( dt0.recorded ) and dt0.nodeid = 'house0'

Если лефт джойн не нужен, то лучше написать inner join.

2. Группировка по минутам.
Для какой благородной цели служит DeviceTimesByMinutes?
Избавиться от секунд и получить все минуты, для которых есть хоть какие-то показания? Ну хорошо, предположим.
Дальше для каждой полученной минуты мы находим все показания первого датчика и перемножаем их на показания второго датчика.
А что будет, если для первого датчика для некой минуты есть две строки и для второго две строки? В результате получится 4 строки.
Врятли это ожидаемый результат. Либо ошибка еще не найдена, либо датчики сбрасывают свои показания реже, чем раз в минуту (пока что).

Напишите, что должен вернуть запрос и опишите чуть-чуть контекст.
К сожалению, в действительности все выглядит иначе, чем на самом деле.
Отредактировано 14.05.2015 18:28 Tigor . Предыдущая версия .
Re: Проблема с MS SQL 2012 после перехода SQL 2005
От: Olaf Россия  
Дата: 15.05.15 05:51
Оценка: 64 (2)
Здравствуйте, gwg-605, Вы писали:

G6>Вылезла неожиданно проблема с MS SQL 2012 после перехода с MS SQL 2005. Запрос стал выполняться 40-50 сукунд вместо нескольких милисекунд.

G6>...
G6>Две Table Spool операции расщиперивают данные почти до 19 миллионов строк

G6>Добавил дополнительные условия по времени для каждого join-a, вроде проблема уменьшилась работало около 5-6 секунд, не решение — но не так проблемно. Через какое-то время добавилось записей в таблицу и проблема возобновилась. Сейчас в таблице 134000 строк.


G6>Запускал аналайзер, он сказал что ничего не может соптимизировать.


G6>Куда копать. Я реально сиквелом уже лет пять не знимался.


1. Статистики проверяли, не устарели они случайно ? Перешли давно на 2012 сервер или сейчас начало эксплуатации ?

2. При просмотре кода возникла мысль везде заменить активно использующуюся медленную скалярную функцию на table valued. Сама функция выглядит так
CREATE FUNCTION [dbo].[fn_RoundToMinutes2]( @date AS DATETIME ) 
RETURNS table 
AS 
  return select DATEADD(ss, -DATEPART(ss, @date), @date) as rtm; 
GO

Использовать можно следующим образом
CREATE VIEW [dbo].[DeviceTimesByMinutes2] 
AS 

select rtm as dg 
from dbo.DeviceValues dv 
cross apply dbo.fn_RoundToMinutes2(dv.recorded) 
group by rtm 

GO 

select a.dg dt, dt0.value_int v0, dt1.value_int v1     
from dbo.DeviceTimesByMinutes a  
    join dbo.DeviceValues dt0  
        cross apply dbo.fn_RoundToMinutes2(dt0.recorded) ca1 on ca1.rtm = a.dg 
    join dbo.DeviceValues dt1  
        cross apply dbo.fn_RoundToMinutes2(dt1.recorded) ca2 on ca2.rtm = a.dg 
where ...

Кстати, т.к. у вас стоят условия отбора для таблиц слева и справа от JOIN с условием AND, указание LEFT JOIN лишнее. Именно поэтому я использую CROSS APPLY, в противном случае можно использовать OUTER APPLY в результирующем запросе.

3. Можете предоставить актуальный план запроса в XML формате ?

4. Возможно, стоит отказаться от расчета времени на лету и в качестве альтернативы использовать вычисляемые поля с подходящим набором индексов. Но для этого нужно полное понимание вашей задачи.
Re: Проблема с MS SQL 2012 после перехода SQL 2005
От: Olaf Россия  
Дата: 15.05.15 06:17
Оценка:
Здравствуйте, gwg-605, Вы писали:

G6>...

G6>Две Table Spool операции расщиперивают данные почти до 19 миллионов строк

G6>...


Избежать спулинга в случае простой функции как у вас, можно используя with schemabinding как описано в статье Improving query plans with the SCHEMABINDING option on T-SQL UDFs План запроса изменится и даже уменьшится время выполнения, но на мой взгляд вариант с табличной функцией описанный выше будет все равно производительней.
Re: Проблема с MS SQL 2012 после перехода SQL 2005
От: kgrach Россия  
Дата: 15.05.15 06:24
Оценка:
Здравствуйте, gwg-605, Вы писали:

G6>Куда копать. Я реально сиквелом уже лет пять не знимался.


Для начала обнови статистику на DeviceTimesByMinutes и DeviceValues.

update statistics DeviceTimesByMinutes with FULLSCAN
update statistics DeviceValues with FULLSCAN
Re[2]: Проблема с MS SQL 2012 после перехода SQL 2005
От: gwg-605 Россия  
Дата: 15.05.15 14:03
Оценка:
Здравствуйте, Tigor, Вы писали:

T>Напишите, что должен вернуть запрос и опишите чуть-чуть контекст.

Спасибо за правильные вопросы. Делал в спешке, главное чтобы хоть как-то работало. Буду сейчас исправлять.

Куча датчиков сбравсывают данные в базу. Каждый датчик делает измерение максимум раз в минуту, но может реже. Нужно получить значения выбранных датчиков на общей оси времени разбитой на минуты, если значения датчика для данного времени нет вернуть NULL.

Потенциально в дальнейшем надо иметь возможность менять временную шкалу: минуты, часы, сутки.

Я понял, что с left outer join был неправ, это подправлю, но не уверен, что поможет в плане скорости. Еще думал в сторону table value функции, но практики ее использования у меня нет.
Re[2]: Проблема с MS SQL 2012 после перехода SQL 2005
От: gwg-605 Россия  
Дата: 15.05.15 14:15
Оценка:
Здравствуйте, Olaf, Вы писали:

O>Здравствуйте, gwg-605, Вы писали:


O>1. Статистики проверяли, не устарели они случайно ? Перешли давно на 2012 сервер или сейчас начало эксплуатации ?

Статистику апдейтил, но не помогает. На 2012 сервер перешел можно сказать только сейчас.

O>2. При просмотре кода возникла мысль везде заменить активно использующуюся медленную скалярную функцию на table valued. Сама функция выглядит так

Спасибо за пример, тоже думал о такой возможности, но опыта применения нет.

O>3. Можете предоставить актуальный план запроса в XML формате ?

Вот здесь

O>4. Возможно, стоит отказаться от расчета времени на лету и в качестве альтернативы использовать вычисляемые поля с подходящим набором индексов. Но для этого нужно полное понимание вашей задачи.

Вот здесь
Автор: gwg-605
Дата: 15.05.15
отвечал Tiger-у.
Re[3]: Проблема с MS SQL 2012 после перехода SQL 2005
От: Tigor Россия  
Дата: 15.05.15 15:22
Оценка: +1
Здравствуйте, gwg-605, Вы писали:

G6>Каждый датчик делает измерение максимум раз в минуту, но может реже. Нужно получить значения выбранных датчиков на общей оси времени разбитой на минуты, если значения датчика для данного времени нет вернуть NULL.

То есть, наверно, возможна ситуация, что для некой минуты не будет показаний ни для какого датчика в системе.
Это означает, что (скорее всего) лучше не использовать DeviceValues как основу для шкалы временни.
И нужна ли в принципе такая шкала на уровне сиквела? Её, конечно, можно там создать\сгенерировать в том или ином виде. Есть разные варианты. Но нужно ли?
Не достачно ли просто вытащить показания датчиков и уже на клиенте привязать к временной шкале?

G6>Потенциально в дальнейшем надо иметь возможность менять временную шкалу: минуты, часы, сутки.

В сутках уж наверняка будет для одного датчика масса показаний. Какое из них надо вернуть? Все?


G6>Я понял, что с left outer join был неправ, это подправлю, но не уверен, что поможет в плане скорости. Еще думал в сторону table value функции, но практики ее использования у меня нет.

На самом деле, этот запрос — это классический случай поворота (pivot). Даже оператор такой специальный есть.
По джойну на каждый датчик — это не самый оптимальный способ. Хорошо, что их только два.
В любом случае (и для джойнов и для pivot) лучше сделать то, что предложил Olaf: создать вычисляемую колонку для времени, где секунд не будет.
По ней создать индекс. В идекс добавить показания датчика как include(d) колонку.


В общем, подумайте, а надо ли вам все это и не проще ли подправить того, кто пользуется этими данными.
К сожалению, в действительности все выглядит иначе, чем на самом деле.
Re[4]: Проблема с MS SQL 2012 после перехода SQL 2005
От: gwg-605 Россия  
Дата: 18.05.15 15:44
Оценка:
Здравствуйте, Tigor, Вы писали:

Поступил несколько подругому:
select a.dg dt, dt0.value_int v0, dt1.value_int v1    from DeviceTimesByMinutes a
    left outer join ( select *, dbo.fn_RoundToMinutes( recorded ) as dg from DeviceValues where nodeid = 'house0' and deviceid = 'therm0' and recorded >= '2015-05-14 00:16:17' and recorded < '2015-05-17 00:16:17' ) dt0
        on a.dg =  dt0.dg 
    left outer join ( select *, dbo.fn_RoundToMinutes( recorded ) as dg from DeviceValues where nodeid = 'house0' and deviceid = 'therm1' and recorded >= '2015-05-14 00:16:17' and recorded < '2015-05-17 00:16:17' ) dt1
        on a.dg =  dt1.dg 
where a.dg >= '2015-05-14 00:16:17' and a.dg < '2015-05-17 00:16:17'
order by dt

Вместо Table Spool/Nested Loops стоят Merge Join(Left Outer Join). В общем все работает как и прежде — шутро.

Потом решил заменить left outer join на join, некоторые проблемы с обработкой NULL были, и запустил запрос на выполнение. Запрос выполнялся почти двое суток 8-() посмотрел план и выпал в осадок опять опять появились два Table Spool/Nested Loops: первый ращиперил 4000строк в 17 миллионов, а второй ращиперил запрос до 72 миллиардов строк!!! после чего отфильтровал все до 4000 строк. План в XML-е можно посмотреть тут

Я видимо, что-то упустил между SQL 2005 и SQL 2012. т.е. сейчас я не понимаю поведения SQL-а. Почему он использует Table Spool/Nested Loops для столь небольшого объема данных и с функцией в join?
Re[5]: Проблема с MS SQL 2012 после перехода SQL 2005
От: Tigor Россия  
Дата: 18.05.15 18:24
Оценка:
Здравствуйте, gwg-605, Вы писали:

G6> Почему он использует Table Spool/Nested Loops для столь небольшого объема данных и с функцией в join?


Почему именно в этом случае построился такой план — не могу сказать.
Если отвечать в общем, то это происходит из-за недостатка информации и ошибочном представлении сиквела об объеме данных.
То есть он ожидает, что строк будет немного и планирует какой-нить нестед луп, а на самом деле получается 72 миллиарда и всё умирает.
И в данном случае, если сравнить EstimateRows и ActualRows, то встречаются серьёзные ошибки
2548.09 — ожидалось
17402693 — получилось

734811000 — ожидалось
72377800187 — получилось

286235 — ожидалось
17402693 — получилось

То, что для лефт джойнов план получился более адекватный, я бы тоже считал везением и не полагался на это.
К сожалению, в действительности все выглядит иначе, чем на самом деле.
Re[5]: Проблема с MS SQL 2012 после перехода SQL 2005
От: Olaf Россия  
Дата: 18.05.15 19:39
Оценка:
Здравствуйте, gwg-605, Вы писали:

G6>Вместо Table Spool/Nested Loops стоят Merge Join(Left Outer Join). В общем все работает как и прежде — шутро.


Можете показать и этот план запроса c left join ?
Re[6]: Проблема с MS SQL 2012 после перехода SQL 2005
От: Tigor Россия  
Дата: 18.05.15 23:56
Оценка:
P.S.

Кстати, если внимательно посмотреть на проблемный план — то первые большие уши торчат из шкалы времени.
EstimateRows="112.333"
ActualRows="4279"

Поскольку кол-во минут в искомом интервале и так известно, лучше не мучать этим сиквел.
Не пробывал, но думаю может помочь:
1. Сгенерировать и сохранить шкалу с минутами в отдельную таблицу. Тут есть два варианта. Либо длинная шкала с "каждой" минутой, либо шкала под максимальный интервал, а минуты придется высчитывать на лету по смещению.
Главное, чтобы сиквел четко понимал сколько строк ожидается.
2. Если строки вида "минута, null, null" можно опустить, то непрерывная шкала вообще не нужна. Попробуйте либо full join между двумя датчиками, либо pivot, либо его рукотворный аналог примерно такого вида
select
  dt,
  v0 = max(v0),
  v1 = max(v1)
from
(
    select
        dt = dbo.fn_RoundToMinutes(recorded),
        v0 = case when /* датчик 1 */ then value_int end, 
        v1 = case when /* датчик 2 */ then value_int end
    from
        DeviceValues
    where
        /* фильтр по времени */ and /* датчик 1 или датчик 2 */
) v
group by
    dt


И хранимую вычисляемую колонку я бы всё-таки попробывал добавить для минут.
Хорошо бы еще с уникальным индексом (минута плюс датчик), но боюсь, что рано или поздно какой-нить датчик таки пришлет свои показания два раза за одну минуту. Ну рестарт где-нить какой-нить произойдет секунд за 30, и минута у датчика начнется заново. Но это уже Вам виднее, возможно такое или нет.
К сожалению, в действительности все выглядит иначе, чем на самом деле.
Re[6]: Проблема с MS SQL 2012 после перехода SQL 2005
От: gwg-605 Россия  
Дата: 19.05.15 09:23
Оценка:
Здравствуйте, Olaf, Вы писали:

G6>>Вместо Table Spool/Nested Loops стоят Merge Join(Left Outer Join). В общем все работает как и прежде — шутро.


O>Можете показать и этот план запроса c left join ?

Да вот здесь
Re[5]: Проблема с MS SQL 2012 после перехода SQL 2005
От: Olaf Россия  
Дата: 19.05.15 12:00
Оценка:
Здравствуйте, gwg-605, Вы писали:

G6>Поступил несколько подругому:

G6>...
G6>Вместо Table Spool/Nested Loops стоят Merge Join(Left Outer Join). В общем все работает как и прежде — шутро.

G6>Потом решил заменить left outer join на join, некоторые проблемы с обработкой NULL были, и запустил запрос на выполнение. Запрос выполнялся почти двое суток 8-() посмотрел план и выпал в осадок опять опять появились два Table Spool/Nested Loops: первый ращиперил 4000строк в 17 миллионов, а второй ращиперил запрос до 72 миллиардов строк!!! после чего отфильтровал все до 4000 строк. План в XML-е можно посмотреть тут


G6>Я видимо, что-то упустил между SQL 2005 и SQL 2012. т.е. сейчас я не понимаю поведения SQL-а. Почему он использует Table Spool/Nested Loops для столь небольшого объема данных и с функцией в join?


Не поленился и создал у себя частично вашу структуру. Функция, представление и технику запроса использую ту, что вы приводили. Планы запросов, совпадают не полностью, но они близки к вашей картине. Четыре решения, в таблице всего 1200000 записей, выбираю 78000.

  Запросы
/* 1 - Изначальный запрос со спулингом */ 
select a.dg dt, dt0.id v0, dt1.id v1
from DeviceTimesByMinutes a 
    join DeviceValues dt0 
        on a.dg = dbo.fn_RoundToMinutes( dt0.dt ) 
    join DeviceValues dt1 
        on a.dg = dbo.fn_RoundToMinutes( dt1.dt )    
where 
    a.dg >= '2005-07-01 00:00:01.000' 
    and a.dg < '2005-08-01 00:00:01.000' 
    and dt0.dt >= '2005-07-01 00:00:01.000' 
    and dt0.dt < '2005-08-01 00:00:01.000' 
    and dt1.dt >= '2005-07-01 00:00:01.000' 
    and dt1.dt < '2005-08-01 00:00:01.000'

/* 2 - Заставляем оптимизатор через подсказку inner merge join использовать для соединения данных алгоритм слиянием. Вариант не очень хорош, т.к. ограничивает количество возможных планов запроса для получения данных, но решает проблему использования вложенных циклов и спулинга. */
select a.dg dt, dt0.id v0, dt1.id v1
from DeviceTimesByMinutes a 
    inner merge join DeviceValues dt0 
        on a.dg = dbo.fn_RoundToMinutes( dt0.dt ) 
    inner merge join DeviceValues dt1 
        on a.dg = dbo.fn_RoundToMinutes( dt1.dt )    
where 
    a.dg >= '2005-07-01 00:00:01.000' 
    and a.dg < '2005-08-01 00:00:01.000' 
    and dt0.dt >= '2005-07-01 00:00:01.000' 
    and dt0.dt < '2005-08-01 00:00:01.000' 
    and dt1.dt >= '2005-07-01 00:00:01.000' 
    and dt1.dt < '2005-08-01 00:00:01.000'

/* 3 - Используется табличная функция вместо скалярной */
select a.dg dt, dt0.id v0, dt1.id v1       
from dbo.DeviceTimesByMinutes2 a  
    join dbo.DeviceValues dt0  
        cross apply dbo.fn_RoundToMinutes2(dt0.dt) ca1 on ca1.rtm = a.dg 
    join dbo.DeviceValues dt1  
        cross apply dbo.fn_RoundToMinutes2(dt1.dt) ca2 on ca2.rtm = a.dg 
where 
    a.dg >= '2005-07-01 00:00:01.000' 
    and a.dg < '2005-08-01 00:00:01.000' 
    and dt0.dt >= '2005-07-01 00:00:01.000' 
    and dt0.dt < '2005-08-01 00:00:01.000' 
    and dt1.dt >= '2005-07-01 00:00:01.000' 
    and dt1.dt < '2005-08-01 00:00:01.000'

/* 4 - И напоследок ваш быстрый запрос, который использует производные таблицы и left join */
select a.dg dt, dt0.id v0, dt1.id v1    from DeviceTimesByMinutes a
    left outer join ( select *, dbo.fn_RoundToMinutes( dt ) as dg from DeviceValues where dt >= '2005-07-01 00:00:01.000' and dt < '2005-08-01 00:00:01.000') dt0
        on a.dg =  dt0.dg 
    left outer join ( select *, dbo.fn_RoundToMinutes( dt ) as dg from DeviceValues where dt >= '2005-07-01 00:00:01.000' and dt < '2005-08-01 00:00:01.000') dt1
        on a.dg =  dt1.dg 
where  a.dg >= '2005-07-01 00:00:01.000' 
    and a.dg < '2005-08-01 00:00:01.000'


Планы запросов слишком большие, поэтому приведу вклад каждого решения в общую стоимость.

Решение 1: 41%
Решение 2: 46%
Решение 3: 7%
Решение 4: 6%

Последние два решения показали лучшую стоимость.

Время выполнения запросов

(78100 row(s) affected)

SQL Server Execution Times:
CPU time = 4056 ms, elapsed time = 4621 ms.

(78100 row(s) affected)

SQL Server Execution Times:
CPU time = 3729 ms, elapsed time = 4093 ms.

(78100 row(s) affected)

SQL Server Execution Times:
CPU time = 374 ms, elapsed time = 813 ms.

(78101 row(s) affected)

SQL Server Execution Times:
CPU time = 3354 ms, elapsed time = 4232 ms.


Я конечно, не настаиваю, но лучшее время на моих данных показал запрос с табличной функцией. И при этом план запроса по стоимости близок к вашему решению под номером 4.

З.Ы.
Можно конечно рассмотреть вариант с PIVOT.
Вариант с with schema binding в функции не показал производительности, возможно из-за использования в представлении.
Re[2]: Проблема с MS SQL 2012 после перехода SQL 2005
От: MasterZiv СССР  
Дата: 20.05.15 21:11
Оценка:
Здравствуйте, Olaf, Вы писали:


O>Избежать спулинга в случае простой функции как у вас, можно используя with schemabinding как описано в статье Improving query plans with the SCHEMABINDING option on T-SQL UDFs План запроса изменится и даже уменьшится время выполнения, но на мой взгляд вариант с табличной функцией описанный выше будет все равно производительней.


Да надо просто выпилить все вызовы функций из запроса, подставить выражения функций в запрос в явном виде, и всё.
Функции в запросах -- зло!
Re[3]: Проблема с MS SQL 2012 после перехода SQL 2005
От: Olaf Россия  
Дата: 21.05.15 03:54
Оценка: 1 (1) +1
Здравствуйте, MasterZiv, Вы писали:

O>>Избежать спулинга в случае простой функции как у вас, можно используя with schemabinding как описано в статье Improving query plans with the SCHEMABINDING option on T-SQL UDFs План запроса изменится и даже уменьшится время выполнения, но на мой взгляд вариант с табличной функцией описанный выше будет все равно производительней.


MZ>Да надо просто выпилить все вызовы функций из запроса, подставить выражения функций в запрос в явном виде, и всё.

MZ>Функции в запросах -- зло!

Так я об этом же и говорил выше, что это предпочтительный вариант. Но вместо использования повторяемости кода в самом запросе можно и нужно использовать inline table-valued функцию, которую оптимизатор развернет в выражение. Сравнение ниже.

Запрос 1 – Скалярная функция
Запрос 2 — Табличная функция
Запрос 3 – Прямое использование функции dateadd
  Запросы
select a.dg dt, dt0.id v0, dt1.id v1
from DeviceTimesByMinutes a 
    join DeviceValues dt0 
        on a.dg = dbo.fn_RoundToMinutes( dt0.dt ) 
    join DeviceValues dt1 
        on a.dg = dbo.fn_RoundToMinutes( dt1.dt )    
where 
    a.dg >= '2005-07-01 00:00:01.000' 
    and a.dg < '2005-08-01 00:00:01.000' 
    and dt0.dt >= '2005-07-01 00:00:01.000' 
    and dt0.dt < '2005-08-01 00:00:01.000' 
    and dt1.dt >= '2005-07-01 00:00:01.000' 
    and dt1.dt < '2005-08-01 00:00:01.000'

select a.dg dt, dt0.id v0, dt1.id v1       
from dbo.DeviceTimesByMinutes2 a  
    join dbo.DeviceValues dt0  
        cross apply dbo.fn_RoundToMinutes2(dt0.dt) ca1 on ca1.rtm = a.dg 
    join dbo.DeviceValues dt1  
        cross apply dbo.fn_RoundToMinutes2(dt1.dt) ca2 on ca2.rtm = a.dg 
where 
    a.dg >= '2005-07-01 00:00:01.000' 
    and a.dg < '2005-08-01 00:00:01.000' 
    and dt0.dt >= '2005-07-01 00:00:01.000' 
    and dt0.dt < '2005-08-01 00:00:01.000' 
    and dt1.dt >= '2005-07-01 00:00:01.000' 
    and dt1.dt < '2005-08-01 00:00:01.000'

select a.dg dt, dt0.id v0, dt1.id v1
from 
(
    select DATEADD(ss, -DATEPART(ss, dt), dt) as dg
    from DeviceValues
    group by DATEADD(ss, -DATEPART(ss, dt), dt)
)a 
    join DeviceValues dt0 
        on a.dg = DATEADD(ss, -DATEPART(ss, dt0.dt), dt0.dt) 
    join DeviceValues dt1 
        on a.dg = DATEADD(ss, -DATEPART(ss, dt1.dt), dt1.dt)
where 
    a.dg >= '2005-07-01 00:00:01.000' 
    and a.dg < '2005-08-01 00:00:01.000' 
    and dt0.dt >= '2005-07-01 00:00:01.000' 
    and dt0.dt < '2005-08-01 00:00:01.000' 
    and dt1.dt >= '2005-07-01 00:00:01.000' 
    and dt1.dt < '2005-08-01 00:00:01.000'

Последние два плана полностью идентичные, где в операторе Compute Scalar (Cost: 2%) вычисляется значение через выражение [Expr1004] = Scalar Operator(dateadd(second, -datepart(second,[Demo].[dbo].[DeviceValues].[dt]),[Demo].[dbo].[DeviceValues].[dt])), в отличие от скалярной функции, где определена сама функция [Expr1004] = Scalar Operator([Demo].[dbo].[fn_RoundToMinutes]([Demo].[dbo].[DeviceValues].[dt]))
  План запроса
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.