4

这是我的查询:

PARAMETERS ...
TRANSFORM ...
SELECT ...
...
PIVOT Mytable.type In ("Other","Info");

这是一个交叉查询,我需要用这一行设置所有列标题:PIVOT Mytable.type In ("Other","Info")现在我已经硬编码了标题OtherInfo

但我想动态地做到这一点。所以我想做的是调用一个 vba 函数来返回我需要的所有标题。

像这样的东西:

PIVOT Mytable.type In (myVbaFunction());

所以我的问题是:如何在 sql 查询中调用 vba 函数?

4

5 回答 5

3

对的,这是可能的。
但是,我认为这是不可能的WHERE IN (...)

下面是一个普通WHERE查询的例子:

Public Function Test() As String
    Test = "Smith"
End Function

...接着:

SELECT * FROM Users WHERE Name = Test();

只要函数只返回一个值,它就可以工作。
但我认为不可能让你的函数返回像“Smith, Miller”这样的东西并使用它:

SELECT * FROM Users WHERE Name In (Test());

(至少我不知道该怎么做)

于 2010-02-14T13:10:34.723 回答
1

如果从 PIVOT 子句中排除 IN 列表,则 TRANSFORM 查询会自动为从 SELECT 语句生成的每个 PIVOT 值创建列。可以通过使用“硬编码”(即文字)值指定 IN 表达式来过滤最后的列。这当然可以从其他答案中得知。

通过在 TRANSFORM 需要过滤它之前限制来自 SELECT 查询的数据以...开始,可以实现完全相同的效果。通过这种方式,不仅限于预定义的文字值——而是 JOINS、子查询和/或 VBA 函数的组合也可以预先过滤数据,有效地选择哪些列出现在转换表中。请注意,在 TRANSFORM 查询中不允许使用 HAVING 子句,但它可以在 TRANSFORM 然后选择的另一个查询中使用,因此在 TRANSFORM 之前如何准备数据实际上没有限制。

以下所有内容都会产生相同的结果。首先使用 PIVOT...IN:

TRANSFORM Count(Services.ID) AS [Schedules]
SELECT Agreement.City FROM Agreement INNER JOIN Services ON Agreement.Account = Services.Account
WHERE ( Services.Code = "IS" )
GROUP BY Agreement.City ORDER BY Agreement.City
PIVOT Month([ServiceDate]) In (1,4,12)

在 WHERE 子句中使用 IN 运算符:

TRANSFORM Count(Services.ID) AS [Schedules]
SELECT Agreement.City FROM Agreement INNER JOIN Services ON Agreement.Account = Services.Account
WHERE ( (Month([ServiceDate]) In (1,4,12)) AND Services.Code = "IS" )
GROUP BY Agreement.City ORDER BY Agreement.City
PIVOT Month([ServiceDate])

但与 PIVOT...IN 子句不同,列表也可以是另一个查询:

WHERE ((Month([ServiceDate]) In (SELECT Values FROM PivotValues)) AND Services.Code = "IS" )

最后,使用 VBA 函数(它回答了原始问题):

TRANSFORM Count(Services.ID) AS [Schedules]
SELECT Agreement.City FROM Agreement INNER JOIN Services ON Agreement.Account = Services.Account
WHERE ( ReportMonth([ServiceDate]) AND Services.Code = "IS" )
GROUP BY Agreement.City ORDER BY Agreement.City
PIVOT Month([ServiceDate])

使用标准模块中定义的 VBA 函数:

Public Function ReportMonth(dt As Date) As Boolean
  Select Case Month(dt)
    Case 1, 4, 12: ReportMonth= True
    Case Else:     ReportMonth= False
  End Select
End Function

(iDevlop 已经在评论中提出了这个解决方案,但我认为它没有被理解,需要好的例子。)

于 2017-05-23T22:46:31.260 回答
1

保证列包含在转换中

Andre 指出,我的最后一个答案未能解决显式 PIVOT 列列表的一个功能,即即使数据不包含相应的值,它也能保证列。在许多情况下,正如 David W Fenton 所评论的那样,“即时”生成完整的 SQL 可能同样好。这是一些这样做的模板代码:

Public Function GenerateTransform(valueArray As Variant) As String
  Dim sIN As String
  Dim i As Integer, delimit As Boolean

  If (VarType(valueArray) And vbArray) = vbArray Then
    For i = LBound(valueArray) To UBound(valueArray)
      sIN = sIN & IIf(delimit, ",", "") & valueArray(i)
      delimit = True
    Next i
    If Len(sIN) > 0 Then sIN = "IN (" & sIN & ")"
  End If

  GenerateTransform = "TRANSFORM ... SELECT ... PIVOT ... " & sIN

End Function

Public Sub TestGenerateTransform()
  Dim values(0 To 2) As Integer
  values(0) = 1
  values(1) = 4
  values(2) = 12

  Debug.Print GenerateTransform(values)
  Debug.Print GenerateTransform(vbEmpty) 'No column list 
End Sub

像我的其他答案一样,以下查询允许人们利用各种^ 技术来选择和过滤条件。这种技术不仅可以保证列,还可以对行进行更多控制^^。

^ 尽管 VBA 函数仍然可以在 SQL 中正常使用,但 Access 不允许在 SQL 执行期间使用 VBA 动态添加新的行数据...行必须基于实际的表行。(从技术上讲,可以使用带有文字值的 UNION SELECT 来创建行,但这对于大量数据来说是禁止的,并且不利于任何类型的动态列选择。)因此,以下技术需要使用辅助表来定义/选择列值

第一个查询应用选择标准并进行初始分组。如果您与我的其他答案进行比较,这与原始的 TRANSFORM 查询基本相同——只是没有 TRANSFORM 和 PIVOT。保存并命名此查询[1 Initial Aggregate]

SELECT Agreement.City, Month([ServiceDate]) AS [Month], Count(Services.ID) AS Schedules
FROM Agreement INNER JOIN Services ON Agreement.Account = Services.Account
WHERE (Services.Code = "IS")
GROUP BY Agreement.City, Month([ServiceDate])
ORDER BY Agreement.City

接下来创建一个对所有所需行值进行分组的查询。在此示例中,我选择仅包含初始选择标准中的相同值。^^ 这组值也可以通过基于未过滤的表或其他查询来与先前的选择标准分离。保存并命名此查询[2 Row Headings]

SELECT RowSource.City AS City
FROM [1 Initial Aggregate] AS RowSource
GROUP BY RowSource.City
ORDER BY RowSource.City

创建行标题和包含列标题的辅助表 [PivotValues] 的交叉连接。交叉连接从两个表的每个组合创建行——在 Access SQL 中,它是通过排除所有 JOIN 关键字来完成的。保存并命名此查询[3 Cross Join]

SELECT [2 Row Headings].City AS City, PivotValues.Values AS Months
FROM [2 Row Headings], PivotValues
ORDER BY [2 Row Headings].City, PivotValues.Values;

最后,转换:通过使用 LEFT JOIN,这将包括交叉连接查询中存在的所有列和行。 对于连接选择查询中缺少数据的列和行对,该列仍将被包含(即保证),并以 Null 作为值。 尽管我们已经对初始查询进行了分组,但转换要求我们无论如何都要重新分组——也许有点多余,但对于获得对最终交叉表结果的所需控制来说没什么大不了的。

TRANSFORM Sum([1 Initial Aggregate].Schedules) AS SumOfSchedules
SELECT [3 Cross Join].City AS City
FROM [3 Cross Join] LEFT JOIN [1 Initial Aggregate] ON ([3 Cross Join].Months = [1 Initial Aggregate].Month) AND ([3 Cross Join].City = [1 Initial Aggregate].City)
GROUP BY [3 Cross Join].City
PIVOT [3 Cross Join].Months

仅仅为了使交叉表列动态化,这似乎有点矫枉过正,但值得定义一些额外的查询以完全控制结果。VBA代码可以用来(重新)定义辅助表中的值,从而满足原来使用VBA动态指定列的问题。

于 2017-05-24T18:40:19.253 回答
0

为什么不在表格中添加这些标题并在您的 xtab 查询中加入该表格?
在函数中维护硬编码可能更容易。

于 2010-02-14T14:06:36.153 回答
-2

正如您所说的那样,您正在使用 Access,那么(在我的脑海中)是的,可以在查询中使用 VBA 函数。

检查文档和 MSDN。

于 2010-02-14T12:56:47.043 回答