SQL Server - Решите это с помощью основанного на множестве решения вместо итерации строк - PullRequest
0 голосов
/ 15 ноября 2011

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

Итак, моя текущая проблема заключается в следующем:

Я создаю временную шкалу чего-то, что произошло из нескольких разных источников в базе данных.,Соответствующая информация, которую я извлекаю:

  1. Имя пользователя
  2. Время, связанное с действием

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

Более графический пример:

Пользователь |Время

Пользователь1 |12:00
Пользователь1 |12: 01
Пользователь1 |12: 02
Пользователь1 |12: 03
Пользователь1 |12: 04
Пользователь1 |12: 05
Пользователь1 |12: 06
Пользователь2 |12: 07
Пользователь2 |12: 08
Пользователь2 |12: 09
User2 |12: 10
Пользователь2 |12: 11
Пользователь1 |12: 12
Пользователь1 |12:13

Что бы я хотел:

Время пользователя

Пользователь1 |12:00
User2 |12: 07
Пользователь1 |12:12

Теперь, в коде, я бы получил этот результат в DataTable и перебрал каждую строку в таблице.Затем я проверю значение [Имя пользователя] текущей строки по сравнению со значением [Имя пользователя] предыдущей строки и добавлю только значения текущей строки, если [Имя пользователя] было другим.Похоже, что среди настоящих экспертов по SQL существует общее отвращение к использованию курсора, но я не уверен, что пока что думаю таким образом, так может кто-нибудь помочь мне здесь?

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

Спасибо!

Ответы [ 4 ]

4 голосов
/ 16 ноября 2011

РЕДАКТИРОВАТЬ Требуется еще 1 уровень косвенности для фильтрации по рангу на работу:

select 
  User,Time
from
(
  select *
  from
  (
      Select 
        User,Time, rank() over (partition by u.User order by u.Time) as User_Rank
      from 
        your_table u
  ) UserRanks
) x
where User_Rank = 1
order by Time

Аналогично ответам Аракнида и Ройи, но используя WHERE NOT EXISTS, а не JOIN.

with CTE  as (
    select user, time, row_number() over (order by time) rn from MyTable
)  
select    CTE.user, CTE.time
from CTE CTE1
where not exists (select user, time from CTE CTE2 where CTE1.rn = CTE2.rn - 1 and CTE1.user = CTE2.user)
0 голосов
/ 16 ноября 2011

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

0 голосов
/ 16 ноября 2011

Итерация на основе строк, вероятно, является вашим лучшим решением в SQL Server. Другие разновидности базы данных позволяют создавать примеры значений из предыдущей / следующей строки (оконные функции lag и lead), но SQL Server их не поддерживает.

Вы можете собрать что-нибудь вместе, как это:

with x as (
    select user, time, row_number() over (order by time) rn from source
)
select x.user, x.time
from x left join x prev on prev.rn = x.rn - 1
where prev.user is null or x.user <> prev.user

Однако я подозреваю, что это неудобно и работает отвратительно.

0 голосов
/ 16 ноября 2011

Наконец:

;with CTE  as (
    select user, time, row_number() over (order by time) rn from MyTable
)  
select    CTE.user, CTE.time
from CTE left join CTE other on other.rn = CTE .rn - 1
where other.user is null or CTE .user <> other.user
...