Пользовательская сортировка T-SQL / OrderBy по вычисленному значению как хранимая процедура или табличная функция - PullRequest
2 голосов
/ 25 апреля 2011

У меня есть следующая структура данных.

Таблица:

CalendarEvents

  • CalendarEventID

  • AccountID

  • FromDate (datetimeoffset (7))

  • ToDate (datetimeoffset (7))

CalendarEventRepetition

  • CalendarEventID

  • Понедельник (бит)

  • вторник (бит)

  • Среда (бит)

  • четверг (бит)

  • Пятница (бит)

  • Суббота (бит)

  • Воскресенье (бит)

  • EveryNWeek int

  • EndAfterNOccurences int

Все значения datetimeoffset являются значениями UTC по отношению к DateTime.MIN и используются только для вычисления значений времени.

Мне нужно иметь возможность возвращать предстоящие события CalendarEvents, отсортированные по времени их начала с СЕГОДНЯ. Это требует вычисления параметра ORDER BY. Я никогда не выполнял пользовательскую сортировку t-sql, и любые входные данные были бы очень благодарны.

В доменной логике я делал следующее для вычисления SoonestOccurrence для еженедельных событий:

DateTime startTime = System.TimeZoneInfo.ConvertTimeBySystemTimeZoneId(e.FromDate.LocalDateTime, TimeZone);

foreach(CalendarEvent e in events)
{
            DateTime endTime = System.TimeZoneInfo.ConvertTimeBySystemTimeZoneId(e.ToDate.LocalDateTime, TimeZone);

            DateTime now = System.TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.Now, TimeZone);

            //clear time component 
            now = new DateTime(now.Year, now.Month, now.Day);

            DayOfWeek day = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), b.Day.SelectedValue);

            int nextWeekDaysAdjustment = (int) day - (int)now.DayOfWeek;

            if (nextWeekDaysAdjustment < 0)
            {
                nextWeekDaysAdjustment+=7;
            }

            DateTime adjustedStartTime = startTime.AddDays(nextWeekDaysAdjustment);
            adjustedStartTime = now.AddTicks(adjustedStartTime.Ticks);

            //SoonestOccurrence is a field added in a partical class for CalendarEvent entity
            e.SoonestOccurrence = adjustedStartTime;

}

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

Заранее благодарю за помощь.

Обновление: мне нужно будет ввести идентификатор часового пояса или смещение часового пояса, чтобы настроить GetDate () для вычислений, а также все времена начала сохраняться в UTC.

Ответы [ 3 ]

1 голос
/ 27 апреля 2011

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

Как только вы сможете рассчитать это значение, порядок будет намного проще:

SELECT
  CalendarEventID,
  dbo.GetLatestDate(
    FromDate, ToDate, Monday, Tuesday,
    Wednesday, Thursday, Friday, Saturday, Sunday, 
    EveryNWeeks, EndAfterNOccurences) AS LatestDate
FROM CalendarEvents
ORDER BY 2

Если вы используете SQL 2005 или более позднюю версию и вам удобнее в c #, то вы даже можете использовать скалярную функцию CLR, а не TSQL.

0 голосов
/ 27 апреля 2011

Трудно было написать в поле для комментариев, поэтому здесь оно идет в поле для ответов:

По сути, мне пришлось создать две скалярные функции: NextCalendarEventOccurenceDate (@EventID) и скалярная функция AddDateTimeOffsetMinutes (@ ToDate, @ MinutesDate)

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

Затем я смог выбрать Eventid с соответствующим им ближайшим временем начала и отсортировать его по ближайшему времени старта.

DECLARE @LocationEvents TABLE (accountid INT, eventid INT,starttime DATETIMEOFFSET)
INSERT INTO @LocationEvents
SELECT ce.AccountID, ce.CalendarEventID, dbo.NextCalendarEventOccurenceDate(ce.CalendarEventID)
FROM CalendarEvents ce
INNER JOIN dbo.NearestAccounts(@lat,@long,@miles) la
ON ce.AccountID = la.accountid

тогда я просто возвращаю свою @LocationEvents ORDER'ed BY начальную дату

SELECT accountid, eventid, starttime FROM @LocationEvents ORDER BY starttime

Спасибо за вашу помощь.

Спасибо, @Rob и @Thx за указание в правильном направлении.

0 голосов
/ 25 апреля 2011

Не совсем понятно, что вы ищете.

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

DECLARE @PageNum AS INT;
DECLARE @PageSize AS INT;
SET @PageNum = 1;
SET @PageSize = 2;

DECLARE @CalendarEvent TABLE ( calendarid INT, starttime DATETIME)

INSERT INTO @CalendarEvent
SELECT 1, Getdate()+1
UNION ALL
SELECT 2, Getdate()+2
UNION ALL
SELECT 3, Getdate()+3
UNION ALL
SELECT 4, Getdate()+4
UNION ALL
SELECT 5, Getdate()+5
UNION ALL
SELECT 6, Getdate()+6

;WITH orderedevents AS
(
    SELECT Row_number() OVER(ORDER BY starttime) AS rownum,
          calendarid
      FROM @CalendarEvent
      WHERE starttime > Getdate()
)

SELECT ce.calendarid, starttime
FROM orderedevents oe
INNER JOIN @CalendarEvent ce
ON oe.calendarid = ce.calendarid
WHERE rownum BETWEEN (@PageNum - 1) * @PageSize + 1
             AND @PageNum * @PageSize
             ORDER BY rownum
...