sql – 对选择的记录进行排序,分组和重新排序的问题

底线:

我有一个Sub应该重新排序一组记录,但其核心的查询不是在罕见的特定情况下按预期对记录进行分组和排序.

背景:

我正在开发一个系统升级,教育人员将课程信息发布到我们的内部网.在现有系统和升级系统中,Classes_Dates表包含与日期相关的所有信息,包括“系列”编号.

系列号是(现在仍然)用于对日期进行分组和排序,主要是为了加快前端的页面生成.类可以在给定系列中具有一个或多个(无限制)日期.

在现有系统中,系列号是手动管理的.通常,这不是问题.按顺序输入类,按顺序输入.有时,在时间顺序流程的中间添加一个类,工作人员将手动重新排序序列号以正确分组/排序日期.它起作用,但如果他们不经常使用该系统,新员工难以学习并且现有员工难以保留.

在升级中,我写了一个sub来自动处理组的重新排序.我试图保留这个概念,但埋葬它让工作人员不需要意识到它仍然存在.

这是sub本身,每次添加新的类日期时调用:

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中修复什么?或者有更好的方法来获得相同的最终结果?

后者可能会更好,因为我现在可以看到我再次看到它随着时间的推移不会很好地扩展……

最佳答案 我想你想要:

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

查询的工作原理:

您的问题描述的是您希望以组(同一系列)显示的数据.但是您还希望根据每个组的MIN(ClassStart)对这些组进行排序.

要找到MIN(ClassStart),我们必须使用GROUP BY系列但我们不能这样做,因为那时多个行(同一组)会崩溃成一个.

这就是MIN(ClassStart)OVER(PARTITION BY系列)实现的目标.它计算ClassStart的最小值,就像我们使用GROUP BY Series一样.

点赞