2017-02-16 4 views
1

У меня есть две таблицы с датами, которые я хочу присоединиться к INNER JOIN. Эти таблицы связаны друг с другом с помощью FK, что гарантирует мне, что запись на Tabla A и ее «связанная запись в таблице B» в том же году.Эффективность: MONTH() vs. DATEDIFF()

Короче говоря, я хочу, чтобы две даты были в том же месяце. как объяснено, DATEDIFF() не имеет логического преимущества в моем случае - он никогда не даст мне -12 или 12, потому что год не имеет отношения к уравнению. Мои результаты всегда будут одинаковыми с DATEDIFF или MONTH (я проверял это, конечно).

С этими предположениями - что было бы более эффективным?

SELECT .... 
    FROM DatesA da 
    INNER JOIN DatesB db 
    ON MONTH(da.Date) = MONTH(db.Date) 
    AND [Rest of the join] 

    SELECT .... 
    FROM DatesA da 
    INNER JOIN DatesB db 
    ON DATEDIFF(MM, da.Date, db.Date) = 0 
    AND [Rest of the join] 

Спасибо!

+3

Просто в том же месяце? Как насчет того же года? Первый «SELECT» может дать странные результаты при работе с датами в течение более одного года. –

+0

[Остальное соединение] имеет дело с другой таблицей, которая представляет годы и другие вещи. Поэтому я знаю, что мне не нужно сравнивать годы, только месяцы. – noamyg

+0

@ user3367818 Затем вы должны сравнивать «MONTH() AND [часть остальной части объединения, например YEAR()]' s, с производительностью 'DATEDIFF()'. – GSerg

ответ

1

Сравнение производительности

Для меня, тестирование с набором данных 2508 записей с датами равномерно распространяющихся через один год, и вступление в таблицу к себе, datepart выполняется значительно лучше, чем datediff (разница между datepart и month был negligable, хотя datepart был обычно ~1ms быстрее Это испытание было сделано на SQL 2008 R2 (SP3) Полный код совместно ниже..

--prep 
create table #testDates (d date) 

insert #testDates 
select dateadd(dd,row_number() over (partition by 1 order by number) % 365,'2017-01-01') 
from master.dbo.spt_values a --, master.dbo.spt_values --uncomment this for a larger test set 

select @@VERSION --Microsoft SQL Server 2008 R2 (SP3) - 10.50.6529.0 (X64) 
go 


--test statements 
set statistics time on 
select count(1) --return 1 so we're measuring query time; not the time to return the results 
from #testDates a 
inner join #testDates b 
on month(a.d) = month(b.d) 
set statistics time off 

set statistics time on 
select count(1) 
from #testDates a 
inner join #testDates b 
on datepart(month,a.d) = datepart(month,b.d) 
set statistics time off 

set statistics time on 
select count(1) 
from #testDates a 
inner join #testDates b 
on datediff(MM,a.d,b.d) = 0 
set statistics time off 

--cleanup 
go 
drop table #testDates 

Сроки были: 5ms, 4ms, 3432ms соответственно.

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

Как насчет индексированных данных?

Добавление индекса после заполнения данных улучшило характеристики datediff; хотя только до 3390ms; все еще оставляя его далеко позади остальных.

create index ix_testDates_d on #testDates(d) --create the index after populating the data to ensure it's not fragmented

Другое

  • Другая причина использовать datepart/month над datediff это лучше самодокументирующимся код; то есть он показывает, что вы ищете даты с тем же месяцем; а не даты, когда число месяцев между ними составляет 0 (что то же самое (лет в сторону);., а последний занимает несколько минут больше, чтобы познавательно процесс

  • причина, чтобы использовать datepart над month что datepart соответствует требованиям ANSI.

  • Однако month имеет преимущество перед datepart бытия детерминированной функцией (ссылка: https://stackoverflow.com/a/14851564/361842), который по какой-то причине datepart нет!

  • Также month более интуитивно понятный; то есть осознанно быстрее для людей, чтобы понять.

  • Выбор между datepart и month с учетом незначительной разницы в производительности должен соответствовать вашим другим требованиям и/или стандартам кодирования.

+0

Привет, не могли бы вы взглянуть на мой предложенный подход? Благодарю. – Horaciux

1

EDIT - похоже, подход DateDiff может использовать индексы, поскольку он не является скалярной функцией, обертывающей значение. Быстрое сравнение в моей среде тестовых данных говорит, что DateDiff будет в несколько раз более эффективным.

+0

Забавно, в моем тесте я получил противоположное ... к моему удивлению –

+0

очень странно ... Я собираюсь запросить гораздо больший набор данных и посмотреть, что я получу. –

+0

Я также тестировал его, и казалось, что МЕСЯЦ был быстрее; Я задал вопрос, потому что это показалось мне нелогичным. Обычно я делаю DATEDIFF по умолчанию, и я боялся, что на результаты повлияла инфраструктура (она запускалась на рабочей среде). – noamyg

1

Я основываю свой ответ, используя предыдущий ответ на @JohnLBevan

Это только баллы 1мса. Это sergable soltion и использовать индексы в столбце даты.

«Уловка» заключается в том, чтобы ранее иметь своего рода таблицу календаря (которую я создаю на лету), имея первый и последний день каждого месяца.

create table #testDates (d date) 

insert #testDates 
select dateadd(dd,row_number() over (partition by 1 order by number) % 365,'2017-01-01') 
from master.dbo.spt_values a --, master.dbo.spt_values --uncomment this for a larger test set 

select @@VERSION --Microsoft SQL Server 2008 R2 (SP3) - 10.50.6529.0 (X64) 
go 


create index ix_testDates_d on #testDates(d) 

--test statements 
set statistics time on 
select count(1) --return 1 so we're measuring query time; not the time to return the results 
from #testDates a 
inner join #testDates b 
on month(a.d) = month(b.d) 
set statistics time off 

select min(d) iniDay,max(d) endDay into #months from #testDates 
group by month(d) 


set statistics time on 
select count(1) --return 1 so we're measuring query time; not the time to return the results 
from #testDates a 
inner join #months m 
on a.d>= m.iniDay and a.d<=m.endDay 
inner join #testDates b 
on b.d>= m.iniDay and b.d<=m.endDay 
set statistics time off 


--cleanup 
go 
drop table #testDates 
drop table #months 

время составило 4 мс, 10 мс для календарного стола, 1 мс.

Для 150.000 рядов

(150000 row(s) affected) 

(1 row(s) affected) 
SQL Server parse and compile time: 
    CPU time = 0 ms, elapsed time = 4 ms. 

(1 row(s) affected) 

SQL Server Execution Times: 
    CPU time = 141 ms, elapsed time = 130 ms. 

(12 row(s) affected) 
SQL Server parse and compile time: 
    CPU time = 14 ms, elapsed time = 14 ms. 

(1 row(s) affected) 

SQL Server Execution Times: 
    CPU time = 47 ms, elapsed time = 48 ms. 
+0

Хорошее решение. Вы должны включить '# months' во время запроса, хотя, если это нужно запускать каждый раз, когда запрос будет работать. Тем не менее, если все даты находятся в пределах известного диапазона, и вы можете предварительно заполнить постоянный стол со всеми месяцами, вы получите выгоду без накладных расходов. – JohnLBevan