【问题标题】:UNPIVOT columns using SQL query使用 SQL 查询的 UNPIVOT 列
【发布时间】:2016-07-14 07:36:59
【问题描述】:

我正在提取另一个工作簿,其中包含一个表,该表具有像 ItemCreationDate 这样的列,并且大多数列(总共 28 个这样的列)都以单词“Global”开头。我想要

  1. 将这些“全局”列(包括 ItemCreationDate)拉到 SQL 记录集中, 添加一个名为 Old/New 的新列,然后
  2. UNPIVOT 它们,即将它们堆叠在一起,然后
  3. 在下一列中,列出它们的列项及其计数。
  4. 他们的计数是根据 ItemCreationDate 得出的,其中任何日期 =2015 是新的
  5. 最终输出应如输出表所示。

我附上了一个Sample.xlsx 文件,我在其中展示了我必须如何从数据表开始到达输出选项卡。这个数据表实际上是我想要拉入记录集并吐出表格的输入,如输出表中所示。我不想创建数据透视表,因为它很麻烦而且数据很多,我想要一种替代的 SQL 方法,其中我可以快速聚合数据并将其一次性插入工作表中。

我没有使用 SQL Server,因此无法使用 UNPIVOT 命令或动态 SQL 循环遍历所有“全局”列。

基本上我想形成一个正确的 SQL 字符串,比如......

Dim arrSQL as variant
......
......
RS.Filter="Like Global*"
......
arrSQL = JOIN(RS.Fields, vbCr)

strSQL = "SELECT [arrSQL], IIF(YEAR([ITEM CREATION DATE])>=YEAR(DATE())-1,""NEW"",""OLD"") AS [New/Old]  from [Data$] GROUP BY...."
strSQL = strSQL & " UNION ALL " & vbcr & _
strSQL = strSQL & " ......

现在,在同一记录集上运行 SQL 以减少列并获取所需的数据格式.... 我知道上述内容并不完全正确,但在这些行中有些内容,以便我可以获得正确的输出,如“输出”选项卡中所示。

谁能快速帮忙?

@a_horse_with_no_name 的编辑:

查看示例文件的屏幕截图:

  1. 数据表: 这实际上是我想拉入记录集的输入工作簿中的表。查看我想要取消透视的各种“全局”列标题及其项目。

  1. 这是我每次都必须创建的两张中间表“新”和“旧”(实际上我想去掉)。 2015 年或以后发现的任何物品都放入新品中,而其余物品则放入旧品中。

  1. JFYI,在输出列中手动使用的公式是:

C 列(新):

=COUNTIF(INDEX(New!$A:$D,0,MATCH($A2,New!$1:$1,0)),Output!$B2)

D 列(旧):

=COUNTIF(INDEX(Old!$A:$D,0,MATCH($A2,Old!$1:$1,0)),Output!$B2)

E 列(% 新):

=Output!C2/SUM(C$2:C$6)

F 列(旧百分比):

=Output!D2/SUM(D$2:D$6)

G 列(索引):

=IF(AND(E2<=0,F2<=0),0,IF(AND(E2>0,F2>0),E2/F2,1))

希望这会有所帮助。

【问题讨论】:

  • 正在使用哪个 DBMS?
  • 我只使用带有 ADO 的 Excel VBA,而不是 Access 或 SQL Server。我想将另一个输入工作簿的表拉到 ADODB 记录集中并创建输出格式。不知道该怎么做。
  • 如果要执行 SQL 语句,您必须使用 some DBMS。
  • ADOdb 似乎是 PHP 的抽象层,而不是数据库系统。
  • 对不起,我没有得到你。 PHP从何而来?我正在使用带有 ADO 的 Excel VBA 从另一个工作簿中提取 excel 表。您看过我分享的 Sample.xlsx 工作簿吗? “数据”表实际上是我想要拉入记录集然后吐出最终输出的表,如输出表所示。

标签: sql excel crosstab unpivot vba


【解决方案1】:

确实,您可以使用Jet/ACE SQL Engine(Windows .dll 文件)在 MS Excel 中运行 SQL 查询,这是 MS Access 默认连接的数据存储。因此,所有 PC 上配备的这项技术并不局限于任何一个 Office/Windows 程序。

考虑以下 Excel VBA 宏(如果在 PC 上使用 Excel),它通过 ADO 连接到 ACE,运行三个聚合 SQL 查询的联合(GLOBAL VIT/CALCGLOBAL FLAVOURS em>、GLOBAL FLAVOR GROUP)和有条件的新旧计数/百分比。后一个百分比列对需要子查询。

要正确设置,请执行以下操作:

  1. 确保Item Creation Date 采用 MM-DD-YYYY(美国)或 DD-MM-YYYY(非美国)日期格式,这与上述屏幕截图或文件当前的日期字段格式不同。

    Sub FormatDates() For i = 2 To 2083 Range("A" & i) = CDate(Range("A" & i)) Next i End Sub

  2. 在与保存数据的工作簿不同的工作簿中运行宏。下面假设数据工作簿在名为 Data 的工作表中保存源信息。

  3. 在查询运行工作簿中,创建一个名为 RESULTS 的空白工作表,其中将填充包括列标题在内的查询输出。

VBA 脚本(两个可用的连接驱动程序(已注释掉)和提供程序版本)

Option Explicit

Sub RunSQL()
    Dim cols As Object, datawbk As Workbook, datawks As Worksheet
    Dim lastcol As Integer, i As Integer, j As Variant, output As Variant

    Set cols = CreateObject("Scripting.Dictionary")
    Set datawbk = Workbooks.Open("C:\Path\To\Data\Workbook.xlsx;")
    Set datawks = datawbk.Worksheets("Data")
    lastcol = datawks.Cells(7, datawks.Columns.Count).End(xlToLeft).Column

    For i = 2 To lastcol
         cols.Add CStr(i - 1), datawks.Cells(1, i).Value
    Next i

    datawbk.Close False
    Set datawks = Nothing
    Set datawbk = Nothing

    output = DataCapture(cols)

End Sub

Function DataCapture(datacols As Object)
On Error GoTo ErrHandle
    Dim conn As Object, rst As Object
    Dim strConnection As String
    Dim classSQL As String, itemSQL As String, grpSQL As String, strSQL As String
    Dim i As Integer, fld As Object, d As Variant, lastrow As Integer

    Set conn = CreateObject("ADODB.Connection")
    Set rst = CreateObject("ADODB.Recordset")

    ' Hard code database location and name '
'    strConnection = "DRIVER={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};" _
'                      & "DBQ=C:\Path\To\Data\Workbook.xlsx;"
    strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" _
                       & "Data Source='C:\Path\To\Data\Workbook.xlsx;" _
                       & "Extended Properties=""Excel 12.0 XML;HDR=YES IMEX=1;"";"

    ' OPEN DB CONNECTION '
    conn.Open strConnection

    For Each d In datacols.keys
        strSQL = " SELECT '" & datacols(d) & "' AS [COLUMN], [Data$].[" & datacols(d) & "] AS ITEMS," _
                    & "   SUM(IIF(Year([Item Creation Date]) >= Year(Date()) - 1, 1, 0)) AS NEW," _
                    & " " _
                    & "   SUM(IIF(Year([Item Creation Date]) < Year(Date()) - 1, 1, 0)) AS OLD," _
                    & " " _
                    & "   ROUND(SUM(IIF(Year([Item Creation Date]) >= Year(Date()) - 1, 1, 0)) / " _
                    & "   (SELECT Count(*) FROM [Data$] AS sub" _
                    & "    WHERE Year(sub.[Item Creation Date]) >= Year(Date()) - 1),2) AS NEWPCT," _
                    & " " _
                    & "   ROUND(SUM(IIF(Year([Item Creation Date]) < Year(Date()) - 1, 1, 0)) / " _
                    & "   (SELECT Count(*) FROM [Data$] AS sub" _
                    & "    WHERE Year(sub.[Item Creation Date]) < Year(Date()) - 1),2) AS OLDPCT" _
                    & " FROM [Data$]" _
                    & " GROUP BY [Data$].[" & datacols(d) & "]"

        ' OPEN RECORDSET '
        rst.Open strSQL, conn

        ' COLUMN HEADERS '
        If d = 1 Then
            i = 0
            Worksheets("RESULTS").Range("A1").Activate
            For Each fld In rst.Fields
                ActiveCell.Offset(0, i) = fld.Name
                i = i + 1
            Next fld
        End If

        ' DATA ROWS '
        lastrow = Worksheets("RESULTS").Cells(Worksheets("RESULTS").Rows.Count, "A").End(xlUp).Row
        Worksheets("RESULTS").Range("A" & lastrow + 1).CopyFromRecordset rst

        rst.Close
    Next d

    conn.Close

    MsgBox "Successfully processed SQL query!", vbInformation
    Exit Function

ErrHandle:
    MsgBox Err.Number & " - " & Err.Description, vbCritical
    Exit Function
End Function

输出

【讨论】:

  • 如果您看到我的原始帖子,我已经提到大约有 28 个这样的全局列,我可能不知道它们的名称。我如何在不知道他们的名字的情况下调整 SQL 查询来解释他们?另外,我按照您的指示进行操作,但出现以下错误:err1err2。有什么问题?
  • 我将连接字符串更改为 strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" _ & "数据源='" & strPath & strFile & "';" _ & "扩展属性=""Excel 12.0 XML;HDR=YES IMEX=1;"";"还将 IIF 日期替换为....... IIF(YEAR([ITEM CREATION DATE]) >= YEAR(DATE())-1 和 IIF(YEAR([ITEM CREATION DATE])
  • 但是当我对 sqlstring 进行 debug.Print 并在同一个数据表的访问中运行它时,查询会返回数据。有什么问题?另外,百分比可以用 SQL 格式化到小数点后 2 位吗?
  • 有趣。我添加了相同的连接字符串和IIF() 逻辑,没有任何问题。见更新。您可能正在使用与您发布的 OneDrive 版本或不同操作系统环境不同的更完整的工作簿。我使用的是 Windows 10/64 位/Office 2013。虽然两者都使用相同的后端 SQL 引擎,但 MS Access UI 查询允许 VBA functions 并非都可用于 Jet/ACE ODBC 查询。
  • 对于 28 列,您可以添加另一个记录集,该记录集循环不同的列值并迭代传递到聚合查询记录集。它们是按另一张表的列或行排列的吗?此外,当您可以使用 Access 并且此类查询处理更加原生和流畅(无 ADO)时,您为什么要在 Excel 中执行此工作?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-13
  • 2016-05-14
  • 1970-01-01
  • 2021-09-24
相关资源
最近更新 更多