2013-12-07 2 views
2

Так что я пытаюсь создать представление, которое автоматически извлекает данные из последних двенадцати месяцев (начиная с конца прошлого месяца.)Эффективный вид SQL с автоматическим диапазоном дат на основе GetDate

Когда я запускаю это с a где:

WHERE Visit_Date between 'Dec 1 2012' and 'Dec 1 2013' 

Он будет работать через ~ 1 мин.

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

WHERE Visit_Date between DATEADD(mm,-12,DATEADD(mm,DATEDIFF(mm,12,GETDATE()),0)) 
       and Dateadd(dd,-1,DATEADD(mm,DATEDIFF(mm,12,GETDATE()),0)) 

Запрос выполняется на столе с 50 миллионами записей. Я уверен, что это более эффективный способ сделать это. Я угадываю, что происходит, это его выполнение через вычисления Getdate() для каждой строки, что, очевидно, не идеально.

Любые предложения? Пожалуйста, имейте в виду, что я создаю представление, и обычно я не пишу хранимые процедуры или динамический SQL.

+1

У вас есть индекс в столбце Visit_Date? –

+1

1) Надеюсь, у вас есть столбец 'DATE' или столбец [SMALL] DATETIME с '00: 00: 00.000 'как время. 2) Это выражение 'Dateadd (dd, -1, DATEADD (mm, DATEDIFF (mm, 12, GETDATE()), 0))' возвращает последний день предыдущего месяца (мне: '2013-11-30 00: 00: 00.000'), а не в первый день текущего месяца (я: ''1 декабря 2013 года') 3) Вы должны опубликовать планы выполнения для обоих запросов. –

+0

Также, если вы используете SQL Server 2012, вы можете создать индекс ColumnStore, он действительно делает некоторые чудеса –

ответ

0

Поскольку вы можете возвращать строки в < 1 минута в таблице с 50M + строками, я предполагаю, что у вас есть индекс в столбце Visit_Date. В первом запросе генератор плана запросов SQL выполняет поиск по индексу, поскольку он имеет приблизительное представление о том, сколько строк будет возвращено, поскольку оно знает границы дат. Затем он делает определение, что индекс стремится к индексу - лучший план действий.

В своем втором запросе он не знает или не знает точно, сколько строк может быть возвращено, поэтому, вероятно, он захочет выполнить сканирование индекса или таблицы вместо поиска.

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

Что-то еще, о чем следует помнить, заключается в том, что если вы указали подсказку индекса, SQL Server будет использовать этот индекс. Если промежуток времени между датой начала и окончания таков, что возвращается больший процент вашей таблицы, поиск может быть не таким эффективным, как сканирование (поэтому выбор иногда выбирается SQL Server).

Ваш лучший друг здесь анализирует оценочный план запроса, который сгенерирован. Вы можете получить это в SSMS. Я бы экспериментировал с несколькими подходами, пока вы не сможете получить поиск по индексу (а не сканирование) по вашему запросу.

2

Я думаю, что @Andriy, вероятно, прав (I've also blogged about it), что это связано с ошибкой оценки мощности (даты меняются при выполнении оценок). Вы можете увидеть больше деталей в KB #2481274, Connect #630583 и вопрос @Andriy отметил:

Query runs slow with date expression, but fast with string literal

Для чего вы только изменить один раз в месяц, я думаю, вы могли бы рассмотреть вопрос о создании работы, которая меняет вид в начале каждого месяца, жестко кодируя диапазон дат в представлении. Это может быть не страшной альтернативой включению trace flag 4199, который может быть или не быть постоянным исправлением, и может или не может вызывать другие проблемы, если вы включите его во всем мире (в отличие от только сеанса, который выполняет этот запрос - снова нет гарантируйте, что это всегда будет так быстро).Вот вид процесса, который я имею в виду:

CREATE PROCEDURE dbo.AlterThatView 
AS 
BEGIN 
    SET NOCOUNT ON; 

    DECLARE @end DATE = DATEADD(DAY, 1-DAY(GETDATE()), GETDATE()); 

    DECLARE @start DATE = DATEADD(MONTH, -12, @end); 

    DECLARE @sql NVARCHAR(MAX) = N'ALTER VIEW dbo.ViewName 
    AS 
     SELECT ... 
     WHERE Visit_Date >= ''' + CONVERT(CHAR(8), @start, 112) + ''' 
     AND Visit_Date < ''' + CONVERT(CHAR(8), @end, 112) + ''';'; 

    EXEC sp_executesql @sql; 
END 
GO 

Просто создать работу, которая работает, скажем, через минуту после полуночи каждого 1-го числа месяца, и называет эту процедуру. Возможно, вам захочется, чтобы задание также выполнялось с SELECT.

don't use BETWEEN for date range queries и stop using lazy shorthand for dateparts.

+0

Спасибо за подробный ответ. Мне удалось реализовать ваше решение SPROC. У меня на самом деле есть несколько связанных взглядов с этой проблемой. Могу ли я изменить их все в одном SPROC? Могу ли я просто объявить второй @ SQL_2? – JKitz

+0

@ JKitz да, вы можете изменить их все в одной процедуре (это не «SPROC»), но вам не нужно несколько переменных. Просто установите новую команду 'ALTER' в' @ sql' и выполняйте ее каждый раз. –

Смежные вопросы