Проблемы с сортировкой, группировкой и изменением порядка выбора записей - PullRequest
4 голосов
/ 28 июня 2011

Итог:

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

Справочная информация:

Я разрабатываю обновление для системы образования, чтобы сотрудники учебного заведения могли размещать информацию о классе в нашей интрасети.В существующей и обновленной системе таблица Classes_Dates содержит всю информацию, относящуюся к дате, включая номер «Серии».

Номер серии использовался (и остается) для группировки и сортировкидаты, в основном для ускорения генерации страниц на переднем конце.Классы могут иметь одну или несколько (без ограничений) дат в данной серии.

В существующей системе номер серии управляется вручную.Обычно это не проблема.Классы вводятся последовательно, в порядке их появления.Иногда класс добавляется в середине хронологического потока, и сотрудники вручную переупорядочивают номера серий, чтобы правильно группировать / сортировать даты.Это работает, но трудно научиться новому персоналу, а существующему персоналу сохранить, если они не часто используют систему.

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

Вот сама подпрограмма, вызываемая каждый раз, когда добавляется новая дата урока:

Sub ReorderGroups(intClassID)
    strSQL = "SELECT DateID, Series, ClassStart "
    strSQL = strSQL & "FROM Classes_Dates "
    strSQL = strSQL & "WHERE ClassID = " & intClassID & " "
    strSQL = strSQL & "GROUP BY Series, ClassStart, DateID "
    strSQL = strSQL & "ORDER BY ClassStart;"

    Dim objSQLDB : Set objSQLDB = CreateObject("ADODB.Command")
    objSQLDB.ActiveConnection = strSQLConn

    Dim objDates : Set objDates = Server.CreateObject("ADODB.Recordset")
    objDates.Open strSQL, strSQLConn, adOpenDynamic, adLockReadOnly, adCmdText
    If Not objDates.BOF Then objDates.MoveFirst
    If Not objDates.EOF Then
        Dim intNewSeries : intNewSeries = 1
        Dim intCurrentOld : intCurrentOld = cLng(objDates("Series"))

        Do Until objDates.EOF
            If intCurrentOld <> cLng(objDates("Series")) Then
                intNewSeries = cLng(intNewSeries) + 1
                intCurrentOld = cLng(objDates("Series"))
            End If

            objSQLDB.CommandText = "UPDATE Classes_Dates SET Series = " & intNewSeries & " WHERE DateID = " & objDates("DateID")
            objSQLDB.Execute ,,adCmdText

            objDates.MoveNext
        Loop
    End If
    objDates.Close
    Set objDates = Nothing
    Set objSQLDB = Nothing
End Sub

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

Саб отлично работает до тех пор, пока нет двух серий с перекрывающимися датами.Следующее:

SELECT DateID, Series, ClassStart
FROM Classes_Dates
WHERE ClassID = 11
GROUP BY Series, ClassStart, DateID
ORDER BY ClassStart;

Собирает этот набор результатов:

DateID  Series  ClassStart
------  ------  --------------
49  1   20100907080000
51  1   20100913080000
50  1   20100916080000
56  2   20100921080000
57  2   20100927080000
58  2   20100929080000
'-- snip --'
670 12  20110614080000
671 12  20110615080000
672 13  20110705080000
676 15  20110707080000
674 14  20110709090000
673 13  20110714080000
675 14  20110716080000

Вместо того, что я ожидал:

DateID  Series  ClassStart
------  ------  --------------
49  1   20100907080000
51  1   20100913080000
50  1   20100916080000
56  2   20100921080000
57  2   20100927080000
58  2   20100929080000
'-- snip --'
670 12  20110614080000
671 12  20110615080000
672 13  20110705080000
673 13  20110714080000
676 15  20110707080000
674 14  20110709090000
675 14  20110716080000

Что мне нужно исправить вSQL?Или есть лучший способ получить тот же конечный результат?

Последнее, вероятно, будет лучше, как я вижу сейчас, когда я смотрю на него снова, это не будет хорошо масштабироваться с течением времени ...* +1036 *

1 Ответ

2 голосов
/ 28 июня 2011

Я думаю, что вы хотите:

SELECT DateID, Series, ClassStart
FROM Classes_Dates
WHERE ClassID = 11
GROUP BY Series, ClassStart, DateID
ORDER BY MIN(ClassStart) OVER(PARTITION BY Series) 
       , ClassStart

Обратите внимание, что если (Series, ClassStart, DateID) является уникальным ключом в этой таблице, вам даже не понадобится GROUP BY:

SELECT DateID, Series, ClassStart
FROM Classes_Dates
WHERE ClassID = 11
ORDER BY MIN(ClassStart) OVER(PARTITION BY Series) 
       , ClassStart

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

SELECT DateID, Series, ClassStart
FROM Classes_Dates
WHERE ClassID = 11
ORDER BY MIN(ClassStart) OVER(PARTITION BY Series) 
       , Series
       , ClassStart

Как работает запрос:

Ваша проблема описывает то, что вы хотите, чтобы данные отображались в группах (одинаковых Series). Но вы также хотите, чтобы эти группы были упорядочены в зависимости от MIN(ClassStart) каждой группы.

Чтобы найти MIN(ClassStart), нам нужно было бы использовать GROUP BY Series, но мы не можем этого сделать, потому что тогда множественные строки (из одной группы) свернутся в одну.

Это то, что MIN(ClassStart) OVER(PARTITION BY Series) достигает. Он вычисляет minimum of ClassStart , как если бы мы использовали GROUP BY Series.

...