Call function for specifying columns in SQL TRANSFORM query

后端 未结 5 1551
情歌与酒
情歌与酒 2020-12-21 05:33

Here is my query:

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

This is a cross query and I need

5条回答
  •  情歌与酒
    2020-12-21 06:10

    Guaranteed Column Inclusion in Transform

    Andre pointed out that my last answer failed to address one feature of the explicit PIVOT column list, namely that it gaurantees columns even when the data does not include corresponding values. In many cases it might be just as well to generate the full SQL "on-the-fly" as David W Fenton commented. Here's some template code for doing so:

    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
    

    Like my other answer, the following queries allow one to utilize various^ techniques in selecting and filtering the criteria. Not only can this technique guarantee columns, this also allows more control of the rows^^.

    ^ Even though VBA functions can still be used to their usual extent in the SQL, Access does not allow new row data to be dynamically added during SQL execution using VBA... rows must be based on actual table rows. (Technically one can use a UNION SELECT with literal values to create rows, but this is prohibitive for a lot of data and does not facilitate any sort of dynamic column selection.) Hence, the following technique requires use of an auxillary table for defining/selecting column values.

    The first query applies selection criteria and does initial grouping. If you compare to my other answer, this is essentially the same as the original TRANSFORM query--only without the TRANSFORM and PIVOT. Save and name this query [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
    

    Next create a query that groups on all desired row values. In this example, I choose to include only the same values from the initial selection criteria. ^^ This set of values could also be decoupled from the previous selection criteria by basing it off the unfiltered table or another query. Save and name this query [2 Row Headings]:

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

    Create a cross join of the row headings and the auxiliary table [PivotValues] containing the column headings. A cross join creates rows from every combination of the two tables--in Access SQL it is accomplished by excluding all JOIN keywords. Save and name this query [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;
    

    Finally, the transform: By using a LEFT JOIN, this will include all columns and rows that exist in the cross join query. For column and row pairs that are missing data in the joined select query, the column will still be included (i.e. guaranteed) with Null as the value. Even though we have already grouped on the initial query, the transform requires that we re-group anyway--perhaps a bit redundant but not a big a deal to obtain the desired control over the final crosstab results.

    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
    

    This might seem like overkill just to make the crosstab columns dynamic, but it can be worth defining a few extra queries for complete control over the results. VBA code can be used to (re)define the values in the auxiliary table, thus satisfying the original question of using VBA to dynamically specify the columns.

提交回复
热议问题