Гарантированное включение столбца в преобразовании
Андре указал, что в моем последнем ответе не была рассмотрена одна особенность явного списка столбцов PIVOT, а именно, что он измеряет столбцы, даже если данные не содержат соответствующих значений. Во многих случаях может быть так же хорошо генерировать полный 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 заголовка строки] :
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 для динамического определения столбцов.