将表 rrecordset 传递给 SQL Server
Posted
技术标签:
【中文标题】将表 rrecordset 传递给 SQL Server【英文标题】:Passing a table rrecordset to SQL server 【发布时间】:2019-11-21 10:29:01 【问题描述】:专家们下午好!
我遇到了与平时截然不同的问题。过去,我通过“通过查询”将单行传递给服务器,有时当我需要传递多条记录时,我利用循环函数多次将数据发送到服务器。但是,如果我有超过 40 行记录,则该循环将需要相当长的时间才能完成。我只是想知道是否有一种方法可以在 1 次移动中将表集发送到服务器,而不是使用循环移动 X 次。
这是我在 Access 端使用的附加到表单内按钮的代码(记录源是本地访问表):
Dim db As dao.Database
Dim rs As dao.Recordset
Dim qdf As dao.QueryDef
Dim strSQL As String
Set db = CurrentDb
Set rs = Me.Recordset
rs.MoveLast
rs.MoveFirst
Do While Not rs.EOF
Set qdf = db.QueryDefs("Qry_Send_ClientData") 'local pass through query
strSQL = "EXEC dbo.SP_Client_Referral @JunctionID='" & Me.ClientID & "', @Note='" & Replace(Me.Txt_Note, "'", "") & "', @Value1='" & Txt_Value1 & "', @Value2='" & Txt_Value2 & "'"
qdf.SQL = strSQL
db.Execute "Qry_Send_ClientData"
rs.MoveNext
Loop
Msgbox "All Client Added!", , "Add client"
现在在 SQL 服务器端,我有以下存储过程 (dbo.SP_Client_Referral),它接收来自传递查询的数据并将代码行插入到特定表中
@ClientID AS NVARCHAR(15),
@Note As NVARCHAR(500),
@Value1 As NVARCHAR(50),
@Value2 As NVARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
BEGIN
INSERT INTO dbo.Client_Data(ClientID, Note, Value_1, Value_2)
SELECT @ClientID, @Note, @Value1, @Value2
END
END
对于单条记录甚至多达 10 条记录,此方法相对较快。然而,随着记录数量的增加,所需的时间可能会很长。如果有一种方法可以将表(即使用 SELECT * from LocalTable 的访问端)传递到 SQL 服务器而不是逐行传递肯定会节省大量时间。只是想知道这种方法是否存在,如果存在,我将如何发送表以及我必须在 SP 的 SQL 服务器端使用什么来接收表记录。或者,我可能必须继续使用这种单行方法,并可能使其更高效,以便执行得更快。
非常感谢您的帮助!
【问题讨论】:
请了解参数化。您在该声明中存在一些巨大的注入问题。 您可以使用 VBA 代码创建 JSON(SQL-Server 2016+)或 XML(较低版本)并将其作为单个参数传递。然后您可以使用 SQL-Server 处理 JSON/XML 的能力一次性插入所有内容。 那个 SP 是实际的代码,还是实际的 SP 更多地处理数据?看到这么多用于这样一个基本任务的代码(将记录从本地表插入服务器表)让我很痛苦。是否有理由不在 Access 中使用链接表? 感谢 cmets,我没有使用链接表,因为如果我能提供帮助,我不希望用户在访问级别上与实际 SQL 表进行交互。我希望通过 SP 与表进行交互,以减少可能出错的问题的数量。因此 SP 并通过查询。我会看看 JSON/XML 选项,看看这是否可行:) 感谢您的提示 【参考方案1】:其实最快的方法?
嗯,这似乎很反直觉,我可以给出一个很长的解释来解释为什么。但是,试试这个,你会发现它比你现在运行的运行了 10 倍或更好。事实上,它可能更接近你所拥有的 100 倍。
我们假设我们有一个标准的链接到dbo.Client_Data
的故事。链接可能是Client_Data
,甚至是dbo_Cliet_Data
。
所以,使用这个:
Dim rs As DAO.Recordset
Dim rsIns As DAO.Recordset
If Me.Dirty = True Then Me.Dirty = False ' write any pending data
Set rsIns = CurrentDb.OpenRecordset("dbo_Client_Data", dbOpenDynaset, dbSeeChanges)
Set rs = Me.RecordsetClone
rs.MoveFirst
Do While Not rs.EOF
With rsIns
.AddNew
!ClientID = rs!ClientID
!Note = Me.Txt_Note
!Value_1 = Me.Txt_Value1
!Value_2 = Me.Txt_Value2
.Update
End With
rs.MoveNext
Loop
rsIns.Close
MsgBox "All Client Added!", , "Add client"
请注意上面的一些奖励。我们的代码很干净——我们不必担心诸如日期之类的数据类型,或者您凌乱的报价问题。如果涉及日期,我们可以再次分配而不必担心分隔符。我们还获得了启动注入保护的奖励!
我们还使用了me.RecordSetClone
。这不是必须做的。这将有助于提高性能,但最重要的是当您移动记录指针时,表单记录位置不会尝试跟随。这将消除很多潜在的闪烁。如果该表单上存在当前事件,它还可以消除巨大的问题。
因此,虽然是一个非常好的想法(recordsetclone),但这并不是您将在此处看到的巨大性能提升的主要原因。 RecordSetClone
与me.RecordSet
相同,但您可以“移动”并遍历记录集,而无需跟随主窗体。
因此,实际上,最“基本”的代码方法,并且在没有 SQL Server 的情况下进行访问时可以使用的方法,结果证明是最好的方法。它更少的代码,更少的杂乱代码,并且会为你省去设置 + 构建 SQL Server 存储过程的所有麻烦。您的所有概念都不是必需的,更糟糕的是,它们会导致性能下降。试试上面的概念。
Access 将一次性增加并管理多个插入。与 reocrdsets 相比,始终使用 SQL 更新/插入命令的概念和想法是一个非常巨大的城市神话,许多访问开发人员都为之倾倒。这不是真的。真正的问题是,如果您可以用一个单独的 SQL 更新语句替换大量单独执行的更新的 VBA 循环,那么是的,您领先一步(在某个 VBA 循环上使用一个 SQL 更新)。
但是,如果您必须执行多个操作并且每个操作都在一行上?代替“许多单独的”SQL 更新,然后在这种情况下,(是)绝大多数情况下,记录集将围绕一大堆单独的更新/插入命令运行以实现相同的目标。它甚至没有接近,通过使用上述概念,您可以获得 10 倍甚至 100 倍的性能提升。
【讨论】:
【参考方案2】:您可以尝试将 XML 数据传递给存储过程。
DECLARE @rowData XML
SELECT @rowData = '<data><record clientid="01" Notes="somenotes" Value1="val1" Value2="val2" /><record clientid="02" Notes="somenotes 2" Value1="val1-2" Value2="val2-2" /></data>'
SELECT X.custom.value('@clientid', 'varchar(max)'),
X.custom.value('@Notes', 'varchar(max)'),
X.custom.value('@Value1', 'varchar(max)'),
X.custom.value('@Value2', 'varchar(max)')
FROM @rowData.nodes('/data/record') X(custom)
【讨论】:
以上是关于将表 rrecordset 传递给 SQL Server的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server - 将表传递给函数......如何?这是个好主意吗?
如何使用 JDBC 将表值参数(类数组参数)传递给 Microsoft SQL Server 2008 R2 中的存储过程? [复制]