底线:
我有一个Sub应该重新排序一组记录,但是它的核心问题不是在罕见的特定情况下按预期对记录进行分组和排序。
背景
我正在开发升级系统,教育人员将课程信息发布到我们的Intranet。在现有系统和升级系统中,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中修复什么?或者有更好的方法来获得相同的最终结果吗?
后者可能会更好,因为我现在可以看到我再次看到它随着时间的推移不会很好地扩展......
答案 0 :(得分:2)
我想你想要:
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
一样。