我花了两天时间寻找解决方案,这让我发疯...
首先让我解释一下我在做什么。我们引入了 50 万条记录,这些记录驱动了十几个数据透视表。为了保持工作簿可管理的文件大小,我直接从外部数据连接构建了数据透视表。这是我手动配置的 odbc 连接。
一切正常,我可以在工作簿中点击“全部刷新”,所有数据透视表都会自动更新,太棒了。
但是现在我需要能够通过手动开始和结束日期来限制整个记录集......更改数据透视表上的日期过滤器并不理想,因为它不仅仅是受结束日期影响的单个字段,还有字段需要在透视之前计算,其值取决于涉及结束日期的公式。
经过一整个下午反复崩溃 Excel 后,我发现了如果您的连接直接连接到数据透视表,则不能使用的限制?和指向单元格引用的参数对话框,一旦你关闭这本书,单元格引用就会丢失。
所以我的下一个方法是这样做:
Dim ReportStartDate, ReportEndDate
' Get parameters from Intro sheet
ReportStartDate = "'" & ActiveWorkbook.Worksheets("Intro").Range("$B$1").Value & "'"
ReportEndDate = "'" & ActiveWorkbook.Worksheets("Intro").Range("$B$2").Value & "'"
' There are 3 directpivot odbc connections/caches that need to be modified.
' In each query, the default report-end-date is specified by CURDATE().
' The default report-start-date is specified as '2010-01-01'
' Replace these defaults with the values retrieved above.
Dim cn As WorkbookConnection
Dim odbcCn As ODBCConnection
Dim originalsqltext, newsqltext
For Each cn In ThisWorkbook.Connections ' loop through the connections
If cn.Type = xlConnectionTypeODBC Then
Set odbcCn = cn.ODBCConnection
originalsqltext = odbcCn.CommandText
If odbcCn.Parent = "Calls" Then
newsqltext = Replace(originalsqltext, "CURDATE()", ReportEndDate)
newsqltext = Replace(newsqltext, "'2010-01-01'", ReportStartDate)
ElseIf odbcCn.Parent = "Suboutcomes" Then
newsqltext = Replace(originalsqltext, "CURDATE()", ReportEndDate)
newsqltext = Replace(newsqltext, "'2010-01-01'", ReportStartDate)
ElseIf odbcCn.Parent = "QtyCallsPerDay1" Then
newsqltext = Replace(originalsqltext, "CURDATE()", ReportEndDate)
Else
newsqltext = originalsqltext
End If
odbcCn.CommandText = newsqltext
odbcCn.Refresh
odbcCn.CommandText = originalsqltext
End If
cn.Refresh ' refresh the other connection without modification
Next
Set cn = Nothing
Set odbcCn = Nothing
但是当它到达 odbcCn.CommandText = newsqltext 运行时错误'1004:应用程序定义或对象定义错误时,它给我一个错误。这太没有帮助了......
我验证了 newsqltext 包含我想要的内容,它只是不会分配回 CommandText。
经过一天的谷歌搜索和一些简单的宏录制实验,看起来改变 CommandText 需要像这样的语法
.CommandText = Array( _
"SELECT C1.CALLID, C1.TAKENON, C1A.TAKENAT, CAST(CONCAT(DATE_FORMAT(TAKENON,'%c/%e/%y'),' ',TIME_FORMAT(TAKENAT,'%H:" _
, _
"%i:%s')) AS CHAR) AS CallDateTime, YEAR(C1.TAKENON) AS Year, CEILING(MONTH(C1.TAKENON)/3) AS Quarter, MONTH(C1.TAKE" _
, _
(因为它很大,所以我放弃了其余部分)......起初我认为这是我的问题,因为当我最初尝试录制宏时,我收到“太多行延续”错误,所以我缩短了查询尽可能在替换之前将其减少到 1428 个字符。替换后,它以 1448 个字符结束......但是我如何将它解析为代码想要的数组格式?还是有更好的方法来做到这一点?
我真的不想像这样破坏我的查询,只是为了能够用vba编辑它们,我觉得我只是错过了如何改变CommandText的东西......
我的搜索出现了一些令人不安的事情,例如无法更改 odbc 连接上的 CommandText 的问题,除非您先将其更改为 oledb,然后您可以更改 CommandText,然后将连接更改回odbc ...但那是在 Excel 2010 之前,不再使用这些... http://p2p.wrox.com/excel-vba/29037-cant-set-commandtext-property-if-cache-has- 1-rpt.html
链接到那里的知识库文章http://support.microsoft.com/kb/816562更加令人震惊......当我看到 StringToArray 函数时,我以为我找到了解决方案,但后来我进一步阅读并看到
注意如果您使用共享数据透视表、基于 OLAP 的数据透视表或基于多个合并范围的数据透视表连接到数据库,则前面的代码可能无法按预期工作。
接着
如果工作表上的多个数据透视表派生自同一个数据透视表,则子例程在处理第一个数据透视表后将不起作用。截至 2003 年 3 月,没有针对此问题的已知解决方法。
尽管它指出该文章仅适用于 Excel 2000 到 2003。
我尝试过的另一件事,我想也许我可以使用?参数,只需使用 vba 设置它们...但是当我使用参数创建一个简单的查询,然后在将参数指向新的单元格引用时录制了一个宏,该宏仅包含以下内容: Sub PARAMEDIT5() ' ' PARAMEDIT5 Macro '
'
With ActiveWorkbook.Connections("PARAMEDIT").ODBCConnection
.BackgroundQuery = False
.CommandText = Array("SELECT * FROM Calls1 where TAKENON > ?" _
)
.CommandType = xlCmdSql
.Connection = _
"ODBC;DRIVER={MySQL ODBC 5.1 Driver};UID=xxxxxxx;PWD=xxxxxxxx;SERVER=xxxxxx;PORT=3306;BIG_PACKETS=1;"
.RefreshOnFileOpen = False
.SavePassword = True
.SourceConnectionFile = ""
.SourceDataFile = ""
.ServerCredentialsMethod = xlCredentialsMethodIntegrated
.AlwaysUseConnectionFile = False
End With
With ActiveWorkbook.Connections("PARAMEDIT")
.Name = "PARAMEDIT"
.Description = ""
End With
ActiveWorkbook.Connections("PARAMEDIT").Refresh
End Sub
我尝试使用直接进入枢轴类型的连接以及连接到外部数据源的常规表,我知道它支持参数。
那么......有谁知道参数化共享数据透视缓存 odbc 连接的查询的正确方法是什么?
更新:我试过这个:
Dim cn, originalCn, newCn As WorkbookConnection
Dim odbcCn As ODBCConnection
Dim originalsqltext, newsqltext
Dim connStr As String
For Each cn In ThisWorkbook.Connections ' loop through the connections
If cn.Type = xlConnectionTypeODBC Then
Set odbcCn = cn.ODBCConnection
originalsqltext = odbcCn.CommandText
Set originalCn = cn
connStr = odbcCn.Connection
Select Case odbcCn.Parent
Case "Calls", "Suboutcomes"
newsqltext = Replace(originalsqltext, "CURDATE()", ReportEndDate)
newsqltext = Replace(newsqltext, "'2010-01-01'", ReportStartDate)
Case "QtyCallsPerDay1"
newsqltext = Replace(originalsqltext, "CURDATE()", ReportEndDate)
Case Else
newsqltext = originalsqltext
End Select
Set newCn = ActiveWorkbook.Connections.Add(odbcCn.Parent & "New", "WhoCares", connStr, newsqltext)
Set cn = newCn
cn.Refresh
Set cn = originalCn
newCn.Delete
Else
cn.Refresh ' refresh any other connections without modification
End If
Next
Set cn = Nothing
Set odbcCn = Nothing
Set newCn = Nothing
Set originalCn = Nothing
虽然它似乎在让命令文本达到我想要的效果方面做了我想做的事,但当我逐步完成时,cn.Refresh 什么也没做。如果它正在刷新,但我的枢轴没有更新,我可以看到他们在哪里寻找 Calls1,并且在刷新发生的那一刻,它被命名为 Calls1New,但连接没有做任何事情(查询通常需要几分钟即可完成)。或者也许我不能将它分配给具有相同名称的现有连接?在我设置 cn = newCn 之后,它们看起来完全一样,具有相同的名称。
我会再四处寻找,但如果其他人做过类似的事情,我会很感激更多的帮助。非常感谢您到目前为止所提供的!
编辑:所以我回到原来的
odbcCn.CommandText = newsqltext
cn.Refresh
odbcCn.CommandText = originalsqltext
我还尝试了在http://support.microsoft.com/kb/816562找到的 odbcCn.CommandText = StringToArray(newsqltext) cn.Refresh odbcCn.CommandText = StringToArray(originalsqltext) 。没有一个工作。
我将发布 originalsqltext 和 newsql 文本,因为它们就在错误之前。请注意,如果我手动将原始sqltext 粘贴到查询对话框中,则原始sqltext 工作正常,newsqltext 也是如此
**由于新信息,删除了之前的编辑**
注意 - 我发现一个线程Excel VBA: Update Pivot Sourcedata似乎是一个类似的问题 - 因为我已经测试过尝试分配 odbcCn.CommandText = originalsqltext (它没有以任何方式改变)并且它也失败了。但是,这个线程是从2009年开始的,所以很可能没有使用excel 2010,因为我试图写
For Each pvtC In ThisWorkbook.PivotCaches
name = pvtC.WorkbookConnection.name
originalsqltext = pvtC.CommandText
pvtC.CommandText = originalsqltext
Next
它也在 pvtC.CommandText = originalsqltext 失败了
更新: 我现在确定这与查询本身无关,而是让多个数据透视表指向同一个数据透视缓存的条件。我用简单的查询创建了一个新的外部数据源
SELECT * FROM clientdashboard1.Calls1 WHERE TAKENON BETWEEN '2010-01-01' AND CURDATE()
作为它的查询。我将连接命名为 AlphaTest,并从中创建了一个数据透视表,然后将该数据透视表复制到另一个工作表并使用不同的字段。我修改了我的代码以首先运行:
For Each pvtC In ThisWorkbook.PivotCaches
name = pvtC.WorkbookConnection.name
If name = "AlphaTest" Then
originalsqltext = pvtC.CommandText
pvtC.CommandText = originalsqltext
End If
Next
它在完全相同的时间点失败,pvtC.CommandText = originalsqltext
然后我删除了第二个数据透视表,然后再次通过,它工作了。
然后只是为了好玩,我把我原来的巨大查询放进去,然后又走了过去。有效。但是,它发现了另一个问题……通过代码更改 CommandText 会导致它刷新。所以我最初的计划,进行替换,进行刷新,然后设置回原来的,是行不通的,因为表格将在第二次分配时再次刷新(如果它有效,那就是)。
更新这只会越来越好。我想制作我的数据透视缓存的虚拟副本,也许只有 1 条记录,将每个数据透视表指向它自己的缓存,然后让 vba 更改“真实”缓存的查询,然后循环遍历并将每个数据透视表指向那个一。执行报告任务(复制工作表、断开链接、另存为、关闭是我们通常的做法)。然后回到原来的书中,将所有数据透视表指向它们各自的虚拟缓存。好吧,你知道我拥有的 AlphaTest 数据源吗?我以为 pvtC.CommandText = originalsqltext 实际上改变了查询并导致 AlphaTest 刷新......哦不,我的朋友。它创建了一个名为 Connection 的新连接,它是 AlphaTest 的副本。好的。我该如何使用它?.....我有一些想法要尝试,但如果其他人已经处理过这个问题,请,