4

我花了两天时间寻找解决方案,这让我发疯...

首先让我解释一下我在做什么。我们引入了 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 的副本。好的。我该如何使用它?.....我有一些想法要尝试,但如果其他人已经处理过这个问题,请,

4

5 回答 5

3

在网上做了很多研究之后......我发现这是更新CommandTextODBC 连接属性时的错误。如果您暂时切换到 OLEDB 连接,请更新您的CommandText属性,然后切换回 ODBC,它不会创建新连接。不要问我为什么……这对我有用。

创建一个新模块并插入以下代码:

Option Explicit

Sub UpdateWorkbookConnection(WorkbookConnectionObject As WorkbookConnection, Optional ByVal CommandText As String = "", Optional ByVal ConnectionString As String = "")

With WorkbookConnectionObject
    If .Type = xlConnectionTypeODBC Then
        If CommandText = "" Then CommandText = .ODBCConnection.CommandText
        If ConnectionString = "" Then ConnectionString = .ODBCConnection.Connection
        .ODBCConnection.Connection = Replace(.ODBCConnection.Connection, "ODBC;", "OLEDB;", 1, 1, vbTextCompare)
    ElseIf .Type = xlConnectionTypeOLEDB Then
        If CommandText = "" Then CommandText = .OLEDBConnection.CommandText
        If ConnectionString = "" Then ConnectionString = .OLEDBConnection.Connection
    Else
        MsgBox "Invalid connection object sent to UpdateWorkbookConnection function!", vbCritical, "Update Error"
        Exit Sub
    End If
    If StrComp(.OLEDBConnection.CommandText, CommandText, vbTextCompare) <> 0 Then
        .OLEDBConnection.CommandText = CommandText
    End If
    If StrComp(.OLEDBConnection.Connection, ConnectionString, vbTextCompare) <> 0 Then
        .OLEDBConnection.Connection = ConnectionString
    End If
    .Refresh
End With

End Sub

UpdateWorkbookConnection子例程仅适用于更新 OLEDB 或 ODBC 连接。连接不一定必须链接到数据透视表。它还解决了另一个问题,并允许您更新连接,即使有多个基于同一连接的数据透视表。

要启动更新,只需使用连接对象和命令文本参数调用函数,如下所示:

UpdateWorkbookConnection ActiveWorkbook.Connections("Connection"), "exec sp_MyAwesomeProcedure"

您也可以选择更新连接字符串。

我从不使用?参数,所以我无法确定这是否会解决您的问题,但我怀疑它会。我总是只使用字符串连接将参数直接插入到 CommandText 字符串中。

于 2015-06-24T16:33:19.637 回答
2

我一直使用 ODBC 连接。我首先在工作表上手动建立连接。我确保“启用后台刷新”已关闭。我整天都在做这些事情。这是一些刷新连接的简单代码(根据需要调暗变量):

ActiveWorkbook.Connections("ExampleConnection").Refresh

要更改命令文本:

ActiveWorkbook.Connections("ExampleConnection").ODBCConnection.CommandText = _
"SELECT FILE1.FIELD1 AS ""Name1"", FILE1.FIELD2 as ""Name2""" & chr(13) & "" & chr(10) & chr(13) & "" & chr(10) & _
"FROM SERVER.LIBRARY.FILE1 FILE1" & chr(13) & "" & chr(10) & chr(13) & "" & chr(10) & _
"WHERE FILE1.FIELD1 = 'FILTER'"
ActiveWorkbook.Connections("ExampleConnection").Refresh

要使用运行时变量更改命令文本:

DIM str AS STRING

str = "VARIABLE"

ActiveWorkbook.Connections("ExampleConnection").ODBCConnection.CommandText = _
"SELECT FILE1.FIELD1 AS ""Name1"", FILE1.FIELD2 as ""Name2""" & chr(13) & "" & chr(10) & chr(13) & "" & chr(10) & _
"FROM SERVER.LIBRARY.FILE1 FILE1" & chr(13) & "" & chr(10) & chr(13) & "" & chr(10) & _
"WHERE FILE1.FIELD1 = '" & str & "'"
ActiveWorkbook.Connections("ExampleConnection").Refresh

并且要刷新多个枢轴,如上所述,请确保您的 ODBC 连接“启用后台刷新”复选框未选中,并且您可以全天刷新枢轴:

ActiveWorkbook.Connections("ExampleConnection").Refresh
Sheet1.PivotTables("PivotTable1").PivotCache.Refresh
Sheet1.PivotTables("PivotTable2").PivotCache.Refresh
Sheet2.PivotTables("PivotTable1").PivotCache.Refresh
Sheet2.PivotTables("PivotTable2").PivotCache.Refresh

一个选项,用于动态 WHERE 子句或绕过“太多行”错误:

DIM s AS STRING
DIM f AS STRING
DIM w AS STRING
DIM r AS RANGE
Dim str AS STRING

set r = Sheet1.Range("A1")
str = r.Value
s = "SELECT FILE1.FIELD1 as ""Name1"", FILE1.FIELD2 as ""Name2"""
f = "FROM SERVER.LIBRARY.FILE1 FILE1"

If r.Value = "" Then
   w = ""
Else
   w = "WHERE FILE1.FIELD1 = '" & str & "'"
End If

ActiveWorkbook.Connections("ExampleConnection").ODBCConnection.CommandText = _
s & chr(13) & "" & chr(10) & chr(13) & "" & chr(10) & _
f & chr(13) & "" & chr(10) & chr(13) & "" & chr(10) & _
w
ActiveWorkbook.Connections("ExampleConnection").Refresh
于 2013-09-20T13:24:11.123 回答
0

这不是您问题的真正答案,但无论如何我想提出这些建议。

For Each cn In ThisWorkbook.Connections     ' loop through the connections
    If cn.Type = xlConnectionTypeODBC Then
        Set odbcCn = cn.ODBCConnection
        originalsqltext = odbcCn.CommandText
        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
        odbcCn.CommandText = newsqltext
        odbcCn.Refresh
        odbcCn.CommandText = originalsqltext
    Else ''' this used to be End If
        cn.Refresh ' refresh the other connection without modification
    End If
Next

注意两件事:我使用了 Select Case(但我认为这只是为了更好的代码,它不会改变任何功能)。第二,你确定 cn.Refresh 不应该在 Else 块中吗?也许我误解了代码,但看起来连接会在您重新分配原始 SQL 后刷新。

至于too many line continuations,您可以像这样欺骗 VBA:

somevar = "line 1" & _
          "line 2" & _
          .....
          "line 55"
somevar = somevar & _
          "line 56" & _
          "line 57"

这样,您在技术上就不会局限于固定数量的& _.

现在,对于你真正的问题:1004: Application-defined or object-defined error意味着 SQL 字符串中有一些东西不能被提供者正确解释。你说它看起来很好 - 可以发布内容newsqltext吗?因为确实,看起来您并没有出现编码错误。

于 2013-03-29T08:56:06.870 回答
0

100% 有效

WorkbookConnection wc = book.Connections.Add("SQL-STRING", "", response.ConnectionString, response.SqlString, XlCmdType.xlCmdSql);
pivot = worksheet.PivotTables(1);
pivot.ChangeConnection(wc);
于 2016-01-29T11:18:30.063 回答
0

再会,

当多个数据透视表访问连接时,显然会出现问题。

以下解决方法可以解决问题:

手动(只需一次)

1.) 手动将合适的 ODBC 连接作为文件导出到任何位置。(双击连接并在“定义”处找到底部的按钮)

在 VBA 中:

2.) 将连接添加到您在步骤 1 中导出的工作簿:

myWorkbook.Connections.AddFromFile "\myPath\myODBCConnection.odc"

3.) 编辑添加连接的命令文本

myWorkbook.Connections("nameOfAddedConnectionName").ODBCConnection.CommandText = "SELECT * FROM 不管什么"

4.) 删除数据透视表访问的连接。

myWorkbook.Connections("oldPivotConnectionName").Delete

5.) 将添加的连接重命名为旧连接的名称

myWorkbook.Connections("nameOfAddedConnectionName").Name = "oldPivotConnectionName"

6.) 刷新连接

myWorkbook.Connections("oldPivotConnectionName").Refresh

而已!:-)

于 2016-03-23T10:49:19.493 回答