MS Access TRANSFORM建PIVOT表替换NULL为0报3027错误
Access VBA导出交叉表查询时替换NULL值触发3027错误解决
问题背景
- 开发VBA例程从MS Access数据库导出数据,输出格式需符合PIVOT透视表规范供外部系统调用
- 现有代码运行整体正常,但通过TRANSFORM命令生成透视结果后,部分字段返回NULL值
- 查询
VW_DEMAND_XL使用的SQL语句如下:
TRANSFORM FIRST([VALUE]) SELECT STRLOC AS ROWNAMES, [DESC] AS [TEXT], CODE FROM TB_DEMAND_PVT GROUP BY STRLOC, [DESC], CODE PIVOT [PARAMETER] & PERIOD;
- 上述查询中
PERIOD(周期)数量动态可变,由导出VBA过程调用。其中CT_DEMAND_PVT是生成表查询,负责创建适配TRANSFORM格式要求的源表TB_DEMAND_PVT - 完整导出VBA代码如下:
Private Sub BTN_EXPORTA_DEMAND_Click() Dim rst As DAO.Recordset Dim ApXL As Object Dim xlWBk As Object Dim xlWSh As Object Dim fld As DAO.Field Const xlCenter As Long = -4108 Const xlBottom As Long = -4107 On Error GoTo err_handler Dim SFile As String Dim SName As String Dim QueryNM As String Dim SheetNM As String Dim TableNM As String TableNM = "TB_DEMAND_XL" '检查目标表是否存在,存在则删除 Importa.DeleteIfExists (TableNM) '关闭系统提示 DoCmd.SetWarnings False '执行查询创建源表 DoCmd.OpenQuery "CT_DEMAND_PVT" '恢复系统提示 DoCmd.SetWarnings True QueryNM = "VW_DEMAND_XL" SheetNM = "DEMAND" SPath = Application.CurrentProject.Path DH = Format(Now, "ddmmyyyy_hhmmss") SFile = "\" & SheetNM & "_" & DH & ".xlsx" SName = SPath & SFile Set rst = CurrentDb.OpenRecordset(QueryNM) 'NULL值检查逻辑放置位置 Set ApXL = CreateObject("Excel.Application") '创建目标Excel文件 Set xlWBk = ApXL.Workbooks.Add ApXL.Visible = False '按指定名称保存文件 xlWBk.SaveAs FileName:=SName xlWBk.Worksheets("Planilha1").Name = SheetNM Set xlWSh = xlWBk.Worksheets(SheetNM) xlWSh.Activate '写入表头固定标识 xlWSh.Range("A1") = "TABLE" xlWSh.Range("B1") = "DEMAND" xlWSh.Range("A2") = "*" '定位到表头起始位置 xlWSh.Range("A3").Select '写入字段名 For Each fld In rst.Fields ApXL.ActiveCell = fld.Name ApXL.ActiveCell.Offset(0, 1).Select Next xlWSh.Range("C3") = "!CODE" rst.MoveFirst '定位到数据起始行写入全量数据 xlWSh.Range("A4").CopyFromRecordset rst xlWSh.Range("3:3").Select '表头格式设置 With ApXL.Selection.Font .Name = "Arial" .Size = 12 .Strikethrough = False .Superscript = False .Subscript = False .OutlineFont = False .Shadow = False End With ApXL.Selection.Font.Bold = True With ApXL.Selection .HorizontalAlignment = xlCenter .VerticalAlignment = xlBottom .WrapText = False .Orientation = 0 .AddIndent = False .IndentLevel = 0 .ShrinkToFit = False .MergeCells = False End With '全选单元格自动适配列宽 ApXL.ActiveSheet.Cells.Select ApXL.ActiveSheet.Cells.EntireColumn.AutoFit '取消选中定位到初始单元格 xlWSh.Range("A1").Select xlWBk.Save xlWBk.Close rst.Close Set rst = Nothing MsgBox ("Arquivo exportado!") Exit_SendTQ2XLWbSheet: Exit Sub err_handler: DoCmd.SetWarnings True MsgBox Err.Description, vbExclamation, Err.Number Resume Exit_SendTQ2XLWbSheet End Sub
故障现象
尝试在Set rst = CurrentDb.OpenRecordset(QueryNM)语句后添加如下NULL值处理代码,将空值替换为0:
With rst .MoveFirst Dim objfield Do While Not .EOF For Each objfield In .Fields If IsNull(objfield.Value) Then .Edit objfield.Value = 0 .Update End If Next objfield .MoveNext Loop .MoveFirst End With
代码执行时,只要记录集存在NULL值,就会触发运行时错误3027:无法更新,数据库或对象为只读。
故障原因
TRANSFORM生成的交叉表查询属于聚合类查询,返回的记录集本身是只读的,不支持编辑、更新操作,直接对该类记录集调用.Edit、.Update方法必然触发只读错误,和数据库权限、锁定配置无关。
解决方案
方案1:SQL层面直接替换NULL值(优先推荐)
修改交叉表查询的TRANSFORM逻辑,用Nz函数直接将聚合结果的空值转为0,从数据源层面解决问题,无需额外修改VBA逻辑:
TRANSFORM FIRST(Nz([VALUE], 0)) SELECT STRLOC AS ROWNAMES, [DESC] AS [TEXT], CODE FROM TB_DEMAND_PVT GROUP BY STRLOC, [DESC], CODE PIVOT [PARAMETER] & PERIOD;
注意:如果动态生成的透视列存在整列无匹配数据的情况,SQL层面的Nz无法覆盖这类全空列,需配合方案2处理
方案2:Excel写入环节替换空值
不要尝试修改只读记录集,在数据写入Excel后,直接对Excel单元格区域的空值批量替换为0,效率更高且无只读限制。
在原代码xlWSh.Range("A4").CopyFromRecordset rst语句后添加如下代码即可:
' 批量将写入区域的空单元格替换为0,xlCellTypeBlanks常量值为4,后期绑定无需额外定义 On Error Resume Next xlWSh.Range("A4").CurrentRegion.SpecialCells(4).Value = 0 On Error GoTo err_handler
内容的提问来源于stack exchange,提问作者Samuca
相关产品推荐
相关产品推荐

