【发布时间】:2014-03-03 12:45:49
【问题描述】:
我有一个以 name, age, city 为列的员工表。我想在运行时为从 1 开始的行号显示一列。我在 Access 中使用 SQL。
【问题讨论】:
-
您的查询是什么,有什么问题?
-
结果是否需要可编辑?你有唯一的 ID 吗?
标签: sql ms-access row-number
我有一个以 name, age, city 为列的员工表。我想在运行时为从 1 开始的行号显示一列。我在 Access 中使用 SQL。
【问题讨论】:
标签: sql ms-access row-number
从您的查询中调用以下函数。
Public Function GetNextNum(str As String) As Long
num = num + 1
GetNextNum = num
End Function
需要注意的是,您必须至少有一个参数(即使您不需要),否则该函数只会被调用一次并为所有行返回 1。
在运行查询之前,将全局变量 num 设置为 0。
【讨论】:
您只需要一个函数即可获得一个非常快速甚至“可分组”的行计数器,无论是否自动重置计数器。
查看内嵌 cmets 了解典型用法:
Public Function RowCounter( _
ByVal strKey As String, _
ByVal booReset As Boolean, _
Optional ByVal strGroupKey As String) _
As Long
' Builds consecutive RowIDs in select, append or create query
' with the possibility of automatic reset.
' Optionally a grouping key can be passed to reset the row count
' for every group key.
'
' Usage (typical select query):
' SELECT RowCounter(CStr([ID]),False) AS RowID, *
' FROM tblSomeTable
' WHERE (RowCounter(CStr([ID]),False) <> RowCounter("",True));
'
' Usage (with group key):
' SELECT RowCounter(CStr([ID]),False,CStr[GroupID])) AS RowID, *
' FROM tblSomeTable
' WHERE (RowCounter(CStr([ID]),False) <> RowCounter("",True));
'
' The Where statement resets the counter when the query is run
' and is needed for browsing a select query.
'
' Usage (typical append query, manual reset):
' 1. Reset counter manually:
' Call RowCounter(vbNullString, False)
' 2. Run query:
' INSERT INTO tblTemp ( RowID )
' SELECT RowCounter(CStr([ID]),False) AS RowID, *
' FROM tblSomeTable;
'
' Usage (typical append query, automatic reset):
' INSERT INTO tblTemp ( RowID )
' SELECT RowCounter(CStr([ID]),False) AS RowID, *
' FROM tblSomeTable
' WHERE (RowCounter("",True)=0);
'
' 2002-04-13. Cactus Data ApS. CPH
' 2002-09-09. Str() sometimes fails. Replaced with CStr().
' 2005-10-21. Str(col.Count + 1) reduced to col.Count + 1.
' 2008-02-27. Optional group parameter added.
' 2010-08-04. Corrected that group key missed first row in group.
Static col As New Collection
Static strGroup As String
On Error GoTo Err_RowCounter
If booReset = True Then
Set col = Nothing
ElseIf strGroup <> strGroupKey Then
Set col = Nothing
strGroup = strGroupKey
col.Add 1, strKey
Else
col.Add col.Count + 1, strKey
End If
RowCounter = col(strKey)
Exit_RowCounter:
Exit Function
Err_RowCounter:
Select Case Err
Case 457
' Key is present.
Resume Next
Case Else
' Some other error.
Resume Exit_RowCounter
End Select
End Function
【讨论】:
您有 5 种方法可用。
仅报告 - 运行总和
如果您将此信息用于 Access 报告,有一种简单的方法不需要 VBA 或花哨的 SQL。只需添加一个带有控制源集=1 的文本框,然后将Running Sum 设置为Over All,就完成了。
下面列出的其余方法适用于表单/数据表/记录集
相关子查询
您可以执行相关子查询。这个解决方案是完全独立的,但不是很通用。应该是这样的:
SELECT
(
SELECT COUNT(*)
FROM Employees AS x
WHERE x.EmployeeID <= e.EmployeeID
ORDER BY x.EmployeeID
) AS RowNumber,
e.EmployeeID
FROM Employees AS e;
请注意,由于相关子查询,性能会随着表中记录数量的增加而迅速下降。如果它不应该依赖于 EmployeeID 而是其他东西(例如 HireDate),您可能必须自定义 ORDER BY 子句以获得所需的数字分配
VBA 函数来维护计数,只进记录集
此方法执行速度更快,但只能使用一次;当然不在表单/数据表中,因为在您导航时会不断评估 VBA 函数。因此,这仅适用于以只进方式读取记录集时。使用标准 VBA 模块:
Private Counter As Long
Public Function ResetRowNumber() As Boolean
Counter = 0
ResetRowNumber = (Counter = 0)
End Function
Public Function GetRowNumber(PrimaryKeyField As Variant) As Long
Counter = Counter + 1
GetRowNumber = Counter
End Function
然后在查询中使用:
SELECT
GetRowNumber([EmployeeID]) AS RowNumber,
EmployeeID
FROM Employees
WHERE ResetRowNumber();
注意使用WHERE 隐式调用ResetRowNumber 函数的技巧。请注意,这仅在只有一个查询处于活动状态时才有效;有多个采用行号的查询将导致不正确的结果。但是实现非常简单且速度更快。
VBA 函数保持计数并保存赋值
这比以前的方法更昂贵,但仍然比足够大的表的相关子查询解决方案便宜。这具有在表格/数据表中有用的优点,因为一旦给出数字,它就会再次给出。同样,在标准 VBA 模块中:
Private NumberCollection As VBA.Collection
Public Function ResetRowNumber() As Boolean
NumberCollection = New VBA.Collection
ResetRowNumber = (NumberCollection.Count = 0)
End Function
Public Function GetRowNumber(PrimaryKeyField As Variant) As Variant
On Error Resume Next
Dim Result As Long
Result = NumberCollection(CStr(PrimaryKeyField))
If Err.Number Then
Result = 0
Err.Clear
End If
If Result Then
GetRowNumber = Result
Else
NumberCollection.Add NumberCollection.Count + 1, CStr(PrimaryKeyField)
GetRowNumber = NumberCollection.Count
End If
If Err.Number Then
GetRowNumber = "#Error " & Err.Description
End If
End Function
输入参数PrimaryKeyValue 引用一个不可为空的列(根据定义应该是主键列),这一点很重要。否则,如果它已经被记录在案,我们将无法知道应该给出哪个数字。 SQL与之前的方法类似:
SELECT
GetRowNumber([EmployeeID]) AS RowNumber,
EmployeeID
FROM Employees
WHERE ResetRowNumber();
与之前的方法一样,这一次只适用于一个查询。如果你需要多个查询,那么你需要两倍的层;一个集合来引用哪个查询的集合,然后检查该查询的集合。这可能会有点毛茸茸。您还可以通过Scripting.Dictionary 获得更多信息,所以这是另一种选择。
还请注意,该函数现在返回 Variant,因为它可能会遇到意外错误。因为函数可以被调用多次,可能是数百甚至数千次,我们不能弹出一个消息框,所以我们可以模仿内置函数所做的并返回一个#Error,这与底层不兼容我们真正使用的Long 类型。
升级到 SQL Server 或其他 RDBMS
Access 是一种现象 RAD 工具,用于构建以数据为中心的应用程序。但是,您不一定要使用它的数据库引擎。您可以将您的数据迁移到一个免费的 RDBMS,使用 ODBC 链接并像以前一样继续使用您的 Access 应用程序,并受益于 SQL 的强大功能,包括窗口函数ROW_NUMBER(),它比 VBA 更容易实现.如果您想要做的不仅仅是获取行号,您可能需要考虑是否应该将数据迁移到不同的数据库引擎。
更多参考,this may be helpful。
【讨论】: