How can I sort the columns in a crosstab query, when the column data is dynamic?

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-03 13:57:07

Having encountered the same scenario various times, I prepared a repeatable way to add an In list to the end of the PIVOT clause. Doing so will sort the columns in the crosstab query by order of the elements in the pivotfield In list. Documentation for this construct is available from MSDN. The solution is a procedure that needs triggered by a command button on a form or another event. Please see the screen shots below the Sub.

Public Sub SortPivotColumns(querynameSource As String, queryname As String, SortName As String, SortColumnNameField As String, SortIndexName As String, NonPivotFieldCount As Integer, ParamArray ParamArr() As Variant)

' This sub goes through several steps to effectively adds an In list that sorts the 'Columns' of a crosstab query in MS Access
' 13 November 2012
' E Easterly
'
' This technique uses several components.
' 1) The original unmodified cross tab query (querynameSource)
' 2) The resulting, columns-have-been-sorted query (query)
' 3) An index table which has two columns, a numeric index used for sorting and the column name
' 4) A table or query that can be joined on the column names of the cross tab query to update the index table
'    The name of the table or query would be 'SortName'
'    The field in 'SortName' that the crosstab query columns are joined against is the 'SortColumnNameField'
'    The field in 'SortName' that has the desired order is the SortIndexName
' 5) A number which specifies the count of non-pivot/row heading columns (NonPivotFieldCount)
' 6) An optional array that contains any parameters needed for the query
'
'
'   USE:
'
'   SortPivotColumns "qryCrosstab_Initial", _
'                 "qryCrosstab_Sorted", _
'                 "tblKeyDescriptions", _
'                 "Descriptions", _
'                 "NumericIndexForSorting", _
'                  1
'
'
'
'
Dim rs As DAO.Recordset
Dim db As Database
Dim fld As DAO.Field
Dim sql As String
Dim ColumnHeading As Variant
Dim qdf As QueryDef
Dim qdfSRC As QueryDef
Dim UpdateIndexSQL As Variant

DoCmd.SetWarnings False 'Turn off warnings

Set db = CurrentDb

Set qdfSRC = db.QueryDefs(querynameSource)
Set qdf = db.QueryDefs(queryname)
qdf.sql = qdfSRC.sql

If Not (IsEmpty(ParamArr)) Then
    Dim i As Integer
    For i = 0 To UBound(ParamArr)
        qdf.Parameters(i) = ParamArr(i)
    Next
End If


' First, get the list of fields from the query

Set rs = qdf.OpenRecordset

' Then, create a temporary indexing table
If Not IsNull(DLookup("Name", "MSysObjects", "Name='ttblSortCrosstabColumns' And Type In (1,4,6)")) Then
    db.Execute "DROP TABLE ttblSortCrosstabColumns"
End If

db.Execute "CREATE TABLE ttblSortCrosstabColumns (FieldIndex INTEGER , ColumnName TEXT(250))"

' And populate it with the current index and column names from queryname
  For Each fld In rs.Fields
    If fld.OrdinalPosition > (NonPivotFieldCount - 1) Then
        DoCmd.RunSQL "Insert into ttblSortCrosstabColumns VALUES(" & fld.OrdinalPosition & ", """ & fld.Name & """)"
    End If
  Next fld
  Set fld = Nothing
  rs.Close
  Set rs = Nothing


' Now, the temporary table is joined with the sort table/query and the indexes are updated
UpdateIndexSQL = ("  UPDATE ttblSortCrosstabColumns " & _
                  "  INNER JOIN " & SortName & " ON ttblSortCrosstabColumns.ColumnName=" & SortName & "." & SortColumnNameField & _
                  "  Set ttblSortCrosstabColumns.FieldIndex = [" & SortIndexName & "]")
DoCmd.RunSQL (UpdateIndexSQL)


' Then, the column headings are added to a string to prepare the In list
sql = "SELECT ttblSortCrosstabColumns.ColumnName FROM ttblSortCrosstabColumns ORDER BY ttblSortCrosstabColumns.FieldIndex"
Set rs = db.OpenRecordset(sql)
    rs.MoveFirst
    ColumnHeading = "'" & rs.Fields(0).Value & "'"
    rs.MoveNext

    Do While Not rs.EOF
    ColumnHeading = ColumnHeading & ", '" & rs.Fields(0).Value & "'"
    rs.MoveNext
    Loop

rs.Close
Set rs = Nothing
' db.Execute "DROP TABLE ttblSortCrosstabColumns"

Dim cs As Variant

' Set qdf = db.QueryDefs(queryname)   ' may not need this

' The query is updated with the In list
cs = Left$(qdf.sql, Len(qdf.sql) - 3) & " In(" & ColumnHeading & ");"

qdf.sql = cs

' Take a look at the resulting query sql by uncommenting the below section
' Debug.Print cs


DoCmd.SetWarnings True  'Turn warnings back on

End Sub

In the below screen shot, note the tblKeyDescriptions and the tblPFValues. These are from the question. qryCrosstab_Initial is analogous to the query provided in the above question. The form is used to run the procedure and open the before and after queries.

An integer field (NumericIndexForSorting) was added to tblKeyDescriptions because the sub requires a numeric index for sorting the column names.

Now, inspect the In list highlighted in the SQL view of the initial and sorted queries.

This is all that is needed to order the columns in a crosstab query. Dynamically generating the In list is the purpose of the sub.

Note: the sub needs to be run each time the query is run, so using an event such as a command button On Click event to tie the sequence together is helpful.

Here is a less than perfect solution that uses some Access & Excel:

  1. When making the crosstab, use Table1.Key for the columns.
  2. On a new tab (call it "Lookup"?) in the Excel file, make Table#1
  3. On the first row of the main tab (i.e. where the dataset is pasted) in your Excel file, create a bunch of Vlookup() formulas to look at row#2 and pull in the correct description from your lookup table.
  4. Paste the dataset into row#2. The result will be the table below, where the first row is actually a bunch of Vlookups that pull in the correct column description.
  5. Ask the user to just ignore or delete row#2.

I don't know how complex your script is, but if this data is pasted into the Excel file by automation, then you can just hide row#2 and skip step 6.

    +---------+--------------------------------------+-----------------------------+---------------------------------+
    | P_Key   | This is yet another accounting code! | Yet another accounting code | Hey, this is accounting code X! |        
    +---------+--------------------------------------+-----------------------------+---------------------------------+
    |PasteHere| abR3                                 | Gruu!                       | Kfsg2E                          |
    +---------+--------------------------------------+-----------------------------+---------------------------------+
    | 1001    | 1.1                                  | 1.2                         | 1.0                             |
    +---------+--------------------------------------+-----------------------------+---------------------------------+
    | 1001    | 2.1                                  | 2.2                         | 2.0                             |
    +---------+--------------------------------------+-----------------------------+---------------------------------+
    | 1001    | 3.1                                  | 3.2                         | 3.0                             |
    +---------+--------------------------------------+-----------------------------+---------------------------------+

I'm just tackling this problem. While the offered solution works (according to description) another possible solution is to create the query-def on the fly (like in the described solution) but instead of altering the SQL of the query, just fill the Column Headings property of the query with a dynamically generated string of columnheadings. OP already mentions the workings of the column headings, but not the This seems easier to accomplish and involves nothing but some vba to enumerate the possible values in a string and setting the property

  • generate crosstab querydef
  • enumerate column headings into string
  • set property

So I'm going to try this and see if it works/possible

well....turns out the columnheadings is no actual property of the querydef, but it is automatically put in the SQL.

I thought I was having a bright moment ;)

mrmmickle1

If you know the expected results of your query and can predict the number of columns, the simplest way to order the results of a crosstab query is to specify the correct order in the Column Headings field in the Property Sheet.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!