CTE вместо курсора

Может ли кто-нибудь указать мне на пример использования CTE для перехода через набор записей вместо курсора?

Все примеры, которые мне удалось найти, показывают рекурсию. Мне это не нужно. Я просто хочу пройти через набор записей по одному.

Спасибо.

(РЕДАКТИРОВАТЬ)

Возможно, это лучше спросить по-другому. Я предположил, что CTE или курсор могут быть лучшим способом получить нужные мне данные, но вот пример.

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

2011-05-24 11:06:28.080     CODE1   199 
2011-05-24 11:06:28.080     CODE2   199 
2011-06-08 13:30:14.830     CODE2   209 
2011-06-08 13:30:14.313     CODE1   209 
2011-06-08 13:30:13.840     CODE2   209 
2011-06-08 13:30:13.450     CODE1   209 
2011-06-08 13:30:13.050     CODE2   209 
2010-05-07 10:45:06.800     CODE3   79  
2010-05-07 10:45:04.833     CODE3   79  
2010-10-15 07:30:16.193     CODE3   79  
2010-01-26 13:51:43.923     CODE3   79  
2010-01-26 13:51:43.923     CODE3   79  
2010-01-26 13:51:44.427     CODE3   79  
2010-01-26 13:51:45.103     CODE3   79  
2010-01-26 13:51:45.890     CODE3   79  
2010-01-26 13:51:46.733     CODE3   79  
2010-01-25 12:40:39.737     CODE3   81  
2010-01-25 12:40:40.890     CODE3   81  
2010-01-25 12:40:41.627     CODE3   81  
2010-01-25 12:40:43.277     CODE3   81  
2010-01-25 14:29:08.360     CODE3   81  
2010-01-21 19:36:34.660     CODE3   98  
2010-01-21 19:36:34.843     CODE3   98  
2010-01-21 19:36:35.013     CODE3   98  
2010-01-21 22:27:24.317     CODE3   83  
2010-01-21 22:31:21.443     CODE2   83  
2010-01-22 19:44:28.880     CODE3   83  

И я хочу выбрать из этой таблицы самую старую дату и код для каждого пользователя, возможно ли это сделать с помощью CTE?

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

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

Мне было интересно, будет ли перебор исходной таблицы (отсортированной по идентификатору клиента и дате) и каждый раз, когда я вижу новый идентификатор клиента, вставляющий эту запись во временную таблицу, более чистым решением... тем более, что решение, которое я нахожу, предполагает уникальная комбинация datetime/customerid.

Вот пример того, что я использую сейчас. Это просто похоже на взлом, и мне интересно, будет ли cusor (которого я обычно избегаю) или CTE более чистым решением.

  DECLARE @Table1 TABLE 
(
    ClaimDate datetime, 
    VenueCode nvarchar(50), 
    CustomerID int
)
 INSERT INTO @Table1
  SELECT sc.CreateDate, v.code, sc.CSFCustomerID 
  FROM foo join foo1 on (snip)


DECLARE @Table2 TABLE
  ( 
  ClaimDate datetime,
  VenueCode nvarchar(50),
  CustomerID int)



 INSERT INTO @Table2
  select MIN(ClaimDate), NULL, CustomerID from @Table1 group by CustomerID

  UPDATE ft
  SET ft.SomeCode = t.VenueCode
FROM @Table2 ft
INNER JOIN @Table1 t ON ft.CustomerID = t.CustomerID
AND ft.ClaimDate = t.ClaimDate

Спасибо, и я обещаю, что буду лучше выбирать ответы в будущем.


person BillyPilgrim    schedule 29.06.2011    source источник
comment
CTE не заменяют курсоры. Они больше заменяют временные таблицы. Если вам нужно, например, выполнять определенный код на каждой итерации, то CTE не сильно поможет.   -  person Kirk Woll    schedule 30.06.2011
comment
Почему нужно проходить по одному? Как вы думаете, почему любой тип построчной операции (будь то DECLARE CURSOR или нет) будет выполняться иначе, чем курсор? То, что замедляет его, - это построчная часть, а не часть DECLARE CURSOR.   -  person Aaron Bertrand    schedule 30.06.2011
comment
Да, пожалуйста, объясните свой вариант использования, и мы сможем указать вам решение.   -  person JNK    schedule 30.06.2011


Ответы (2)


Хотя это довольно старый пост и заголовок вопроса не подходит для иллюстрации вашей основной задачи, вот наконец: И я хочу выбрать из этой таблицы самую старую дату и код для каждого пользователя, возможно ли это сделать с помощью КТР?

Ответ: да. Вы можете попробовать это:

-- CTE version of the SQL
WITH CTE (OldestClaimData, CustomerID)
AS
(
  SELECT
    MIN(ClaimDate), CustomerID
  FROM
    UserClaims
  GROUP BY
    CustomerID
)
SELECT DISTINCT
  UC.*
FROM
  UserClaims UC
INNER JOIN
  CTE ON (
    CTE.OldestClaimData=UC.ClaimDate
    AND
    CTE.CustomerID=UC.CustomerID
    AND 
    UC.VenueCode=(
      SELECT TOP 1
        VenueCode
      FROM
        UserClaims sub
      WHERE
        sub.ClaimDate=UC.ClaimDate
        AND
        sub.CustomerID=UC.CustomerID
    )
)
ORDER BY
    UC.CustomerID;

Для вашего удобства я поместил свой и ваш код в SQL Fiddle.

PD: поработайте над скоростью принятия.

person Luis Quijada    schedule 27.09.2012

person    schedule
comment
Ваше решение не работает, если вы добавите в поле VenueCode Mitch. Спасибо хоть. - person BillyPilgrim; 01.07.2011
comment
А что не работает? Строка SELECT Table.* должна возвращать все столбцы из этой таблицы, включая столбец кода... - person therealmitchconnors; 01.07.2011