2012-05-22 4 views
0

Я знаю, что есть несколько сообщений о том, как BAD попытаться выполнить цикл на SQL Server в хранимой процедуре. Но я не совсем понял, что я пытаюсь сделать. Мы используем возможность подключения данных, которая может быть напрямую связана с Excel.Альтернативная альтернатива циклу в SQL Server

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

Мне нужно все custID s, у кого есть заказы прямо перед случаем типа 38,40. Но только получите их, если нет другого порядка между событием и порядком в первом запросе.

Таким образом, существует 3 части. Сначала я запрашиваю для всех заказов (таблицу заказов) на основе временного интервала во временную таблицу.

Select into temp1 odate, custId from orders where odate>'5/1/12' 

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

Select into temp2 eventdate, temp1.custID from LogEvent inner join temp1 on 
temp1.custID=LogEvent.custID where EventType in (38,40) and temp1.odate>eventdate 
order by eventdate desc 

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

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

Select ordernum, shopcart.custID from shopcart right outer join temp2 on 
shopcart.custID=temp2.custID where shopcart.odate >= temp2.eventdate and 
ordernum is null 

Есть ли способ, чтобы упростить это и сделать его набор на основе работать в SQL Server, а не какой-то цикл, который я это выполнить на клиенте?

+0

какая версия sql-сервера? –

+0

Я использую 2005 и 2008 годы. Мы начинаем мигрировать в 2008 году, но не закончили, поэтому мне нужно решить это и в 2005 году. – CaptainBli

+4

Это 1 мая или 5 января? Пожалуйста, используйте безопасные, недвусмысленные форматы для литературных дат, например. ''20120501'' ... SQL Server никогда не будет неверно истолковывать это, а также ваши пользователи, коллеги или читатели здесь. –

ответ

1

Это отличный пример перехода на нотацию на основе набора.

Во-первых, я объединил все три ваших запроса в один запрос. В общем случае, имея один запрос, оптимизатор запросов делает то, что он делает лучше всего - определяет пути выполнения. Это также предотвращает случайную сериализацию запросов на многопоточной/многопроцессорной машине.

Ключ является row_number() для упорядочивания событий, так что последнее имеет значение 1. Это вы увидите в последнем предложении WHERE.

select ordernum, shopcart.custID 
from (Select eventdate, temp1.custID, 
      row_number() over (partition by temp1.CustID order by EventDate desc) as seqnum 
     from LogEvent inner join 
      (Select odate, custId 
      from order 
      where odate>'5/1/12' 
      ) temp1 
      on temp1.custID=LogEvent.custID 
     where EventType in (38,40) and temp1.odate>eventdate order by eventdate desc 
    ) temp2 left outer join 
    ShopCart 
    on shopcart.custID=temp2.custID 
where seqnum = 1 and shopcart.odate >= temp2.eventdate and ordernum is null 

Я сохранил ваши соглашения об именах, хотя я думаю, что «из заказа» должен генерировать синтаксическую ошибку. Даже если это неправда, нужно называть таблицы и столбцы зарезервированными словами SQL.

+0

Да, извините, что это должны быть заказы не на заказ. – CaptainBli

+0

row_number() будет перебирать все события для данных клиентов. Это быстрее, чем цикл и запрос одной строки события для каждого клиента? Для группы клиентов может быть 10000 строк событий. Я пытаюсь лучше понять, это все. – CaptainBli

+1

Да! «Цикл» внутри базы данных безумно быстрее, чем цикл с помощью курсора. Курсоры имеют много накладных расходов, вынося каждое значение взад и вперед из базы данных. Курсоры запускаются последовательно, базы данных параллельно и т. Д. –

0

Если вы используете более новую версию sql-сервера, то вы можете использовать функцию ROW_NUMBER. Я напишу пример в ближайшее время.

;WITH myCTE AS 
( 
SELECT 
    eventdate, temp1.custID, 
    ROW_NUMBER() OVER (PARTITION BY temp1.custID ORDER BY eventdate desc) AS CustomerRanking 
FROM LogEvent 
JOIN temp1 
    ON temp1.custID=LogEvent.custID 
WHERE EventType IN (38,40) AND temp1.odate>eventdate 
) 
SELECT * into temp2 from myCTE WHERE CustomerRanking = 1; 

Это дает вам самое последнее событие для каждого клиента без цикла.

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

+0

Мне нравится эта идея, но ей придется перебирать всю таблицу Event для всех клиентов в моем первоначальном запросе. Это может быть десятки тысяч записей. Где цикл может дать мне одну запись для каждого заказа. – CaptainBli

+1

@CaptainBli Вам нужно думать в наборе логики, а не процедурной логики. Это ограничит ваш запрос в наборах. Я изменил свой ответ, чтобы включить рейтинг клиентов где = 1 в первом запросе с использованием CTE. Оптимизатор должен заботиться обо всем остальном. Циклирование, когда ненужное - это всегда плохая идея в SQL. Вы уже упоминали :) –

+1

@CaptainBli: вы не будете знать, не является ли этот запрос неэффективным, пока вы не посмотрите на его план запроса. При поддержке индексов в запросе могут использоваться операторы Segment и Top, чтобы избежать обработки каждой строки 'LogEvent' из-за предложения CustomerRanking = 1. (Отметьте из моего комментария к вашему вопросу, что, независимо от этого, я думаю, что запрос неверен. Мой комментарий здесь только в ответ на ваше замечание о том, что ему «придется перебирать всю таблицу событий».) –

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