Как вызвать VBA-функцию для указания столбцов в запросе SQL TRANSFORM? - PullRequest
4 голосов
/ 14 февраля 2010

Вот мой запрос:

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

Это перекрестный запрос, и мне нужно установить все заголовки столбцов в этой строке: PIVOT Mytable.type In ("Other","Info"), и теперь я жестко закодировал заголовки, Прочие и Информация .

Но я хочу сделать это динамически. Поэтому я хочу вызвать vba-функцию, которая возвращает все нужные мне заголовки.

Примерно так:

PIVOT Mytable.type In (myVbaFunction());

Итак, мой вопрос: как вызвать vba-функцию внутри sql-запроса?

Ответы [ 5 ]

3 голосов
/ 14 февраля 2010

Да, это возможно.
Однако я не думаю, что это возможно с WHERE IN (...).

Вот пример для обычного WHERE запроса:

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

... а затем:

SELECT * FROM Users WHERE Name = Test();

Это работает, пока функцияВозвращает только одно значениене знаю как это сделать)

1 голос
/ 24 мая 2017

Гарантированное включение столбца в преобразовании

Андре указал, что в моем последнем ответе не была рассмотрена одна особенность явного списка столбцов 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 для динамического определения столбцов.

1 голос
/ 24 мая 2017

Если список IN исключен из предложения PIVOT, запрос TRANSFORM автоматически создает столбцы для каждого значения PIVOT , сгенерированного из оператора SELECT. Последние столбцы можно отфильтровать, указав выражение IN с помощью «жестко закодированных» (то есть литеральных) значений. Это, конечно, известно из других ответов.

Точно такой же эффект может быть достигнут путем ограничения данных из запроса SELECT на начало с * до , которое необходимо выполнить для преобразования. Таким образом, каждый не ограничивается только предопределенными литеральными значениями - скорее комбинации JOINS, подзапросов и / или функций VBA также могут предварительно фильтровать данные, эффективно выбирая, какие столбцы появятся в таблице преобразования. Обратите внимание, что предложение HAVING недопустимо в запросе TRANSFORM, но его можно использовать в другом запросе, который затем выбирает 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)

Использование оператора IN в предложении WHERE:

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 уже предложил это решение в комментарии, но я не думаю, что оно было понято и требуются хорошие примеры.)

0 голосов
/ 14 февраля 2010

Почему бы не добавить эти заголовки в таблицу и не присоединиться к этой таблице в запросе xtab?
Вероятно, проще поддерживать это жесткое кодирование в функции.

0 голосов
/ 14 февраля 2010

Поскольку вы заявили, что используете Access, то (из головы вон) да, возможно использовать функции VBA в запросах.

Проверка документации и MSDN.

...