【问题标题】:Retrieving data from two SQL databases in VBA excel从 VBA excel 中的两个 SQL 数据库中检索数据
【发布时间】:2021-05-15 12:45:28
【问题描述】:

我有这个运行良好的长 SQL 查询,我想通过 Excel VBA 运行它以获取 Excel 中的数据。

为了让我的查询运行,我发现了两个需要解决的问题。

第一个是如何在 VBA Excel 中从两个 SQL 数据库中检索数据,第二个是如何在 VBA 中处理临时表。

我的工作 SQL 查询是

/* Layout: Temporary table containing all the information for Charge Events */
/* Layout: Temporary table containing all the information for Discharge Events */
/* Layout: Joining it all together */

/*Declaring the time frame for the data*/ 
   DECLARE @StartDate nvarchar(10)
   DECLARE @EndDate nvarchar(10)
     
    SET @StartDate ='20/11/2020'
    SET @EndDate ='21/11/2020'

/*Making the temporary table for the Charge data*/

drop table #chargeeventstable   
SELECT  
      [Date1]=  a.[_TimeStamp],[FCE1]='A',
      [Shift]=case when DATEPART(hour, a.[_TimeStamp]) between 7 and 18 then '1' else '2' end ,
      [Avg_Charg_Temp_A]=avg(CASE WHEN a.[FURNACE] ='A' then convert(real,isnull (b.[charge_temperature],'0')) else null end),
      [Charge_slabs_A]=sum(case when a.[FURNACE] ='A' then 1.0 else null end),    
      [NGVolA]=sum(CASE WHEN a.[Furnace] ='A' then convert(real,isnull (a.[NG_AVG_MEAS_FLOW],'0.0')) else 0.0 end ) ,      
      [FCE2]='B' ,
      [Avg_Charg_Temp_B]=avg(CASE WHEN a.[FURNACE] ='B' then convert(real,isnull (b.[charge_temperature],'0')) else null end),
      [Charge_slabs_B]=sum(case when a.[FURNACE] ='B' then 1.0 else '0' end),
         
into #chargeeventstable

 FROM (select a.*, (select min(aa.[_TimeStamp])
            from ix.dbo.Fce_Data aa 
            where aa.[_TimeStamp] > a.[_TimeStamp])as next_time_2
    from ix.dbo.Fce_Data a) a 
    Left JOIN ADB.dbo.Temp_Aims b on b.[charge_time] >= a.[_TimeStamp] and b.[charge_time] < a.[next_time_2] and a.[Furnace] = b.[Furnace]
    
WHERE CONVERT(datetime, a.[_TimeStamp], 103)  BETWEEN CONVERT(datetime, @StartDate , 103) AND CONVERT(datetime, @EndDate , 103)     

Group by a.[_TimeStamp]
ORDER BY a.[_TimeStamp]

/*Making the temporary table for the Discharge data*/
      
drop table #dischargeeventstable    
SELECT  
      [Discharge_slabs_A]=sum(case when b.[FURNACE] ='A' then 1.0 else '0' end),
      [Discharge_slabs_B]=sum(case when b.[FURNACE] ='B' then 1.0 else '0' end),     

into #dischargeeventstable

 FROM (select a.*, (select min(aa.[_TimeStamp])
            from ix.dbo.Fce_Data aa 
            where aa.[_TimeStamp] > a.[_TimeStamp])as next_time_2
    from ix.dbo.Fce_Data a) a 
    Left JOIN ADB.dbo.Temp_Aims b on b.[discharge_time] >= a.[_TimeStamp] and b.[discharge_time] < a.[next_time_2] and a.[Furnace] = b.[Furnace]

WHERE CONVERT(datetime, a.[_TimeStamp], 103)  BETWEEN CONVERT(datetime, @StartDate , 103) AND CONVERT(datetime, @EndDate , 103)     

Group by a.[_TimeStamp]
ORDER BY a.[_TimeStamp]

/*Joining the two temporary tables together*/

SELECT 
      [FCE1],
      a.[Date1],
      [Shift],
      [Charge_slabs_A],
      [Avg_Charg_Temp_A],
      [NGVolA], 
      [FCE2],
      [Charge_slabs_B],
      [Avg_Charg_Temp_B],
      [Discharge_slabs_A],
      [Discharge_slabs_B]
       
 FROM #chargeeventstable a 
    Left JOIN #dischargeeventstable b on a.[Date1] = b.[Date1] 
    

所以我想做的就是将此查询转换为 VBA Excel。 到目前为止,我在 VBA 中所做的是:

Private Sub SetScrollBarRange()
    Dim sht As Worksheet
    Dim LastRow As Long
    
    Set sht = ThisWorkbook.Worksheets("-Input Data-")
    LastRow = sht.Cells(sht.Rows.Count, "C").End(xlUp).Row
    ScrollBarSelDate.Max = LastRow
End Sub

Private Sub ScrollBarSelDate_Change()
    SetScrollBarRange
    ThisWorkbook.Worksheets("A&B").Cells(4, 15) = ScrollBarSelDate.Value

End Sub

Private Sub UpdateButton_Click()

    On Error Resume Next
  
    Sheets("Loading").Select
    Sheets("-Input Data-").Range("A20:AQ1000").ClearContents

    Dim cn As New ADODB.Connection
    Dim rs As New ADODB.Recordset
    Dim cm As New ADODB.Command
    Dim SQLStr As String
    Dim i As Integer
    
    Dim StartDate As String
    Dim EndDate As String
    StartDate = Sheets("A&B Sankey").Cells(2, 18)
    EndDate = Sheets("A&B Sankey").Cells(2, 20)

      SQLStr = "drop table #chargeeventstable"
      SQLStr = SQLStr & "SELECT  [Date1]=  a.[_TimeStamp],[FCE1]='A',[Shift]=case when DATEPART(hour, a.[_TimeStamp]) between 7 and 18 then '1' else '2' end,"
      SQLStr = SQLStr & "[Avg_Charg_Temp_A]=avg(CASE WHEN a.[FURNACE] ='A' then convert(real,isnull (b.[charge_temperature],'0')) else null end),"
      SQLStr = SQLStr & "[Charge_slabs_A]=sum(case when a.[FURNACE] ='A' then 1.0 else null end),"
      SQLStr = SQLStr & "[NGVolA]=sum(CASE WHEN a.[Furnace] ='A' then convert(real,isnull (a.[NG_AVG_MEAS_FLOW],'0.0')) else 0.0 end ) ,"
      SQLStr = SQLStr & "[Date1]=  a.[_TimeStamp],[FCE2]='B',[Shift]=case when DATEPART(hour, a.[_TimeStamp]) between 7 and 18 then '1' else '2' end, "
      SQLStr = SQLStr & "[Avg_Charg_Temp_B]=avg(CASE WHEN a.[FURNACE] ='B' then convert(real,isnull (b.[charge_temperature],'0')) else null end),"
      SQLStr = SQLStr & "[Charge_slabs_B]=sum(case when a.[FURNACE] ='B' then 1.0 else '0' end)"
     
     SQLStr = SQLStr & " into #chargeeventstable"

SQLStr = SQLStr & " FROM (select a.*, (select min(aa.[_TimeStamp])"
            SQLStr = SQLStr & "from ix.dbo.Fce_Data aa"
            SQLStr = SQLStr & "where aa.[_TimeStamp] > a.[_TimeStamp])as next_time_2"
    SQLStr = SQLStr & "from ix.dbo.Fce_Data a) a"
    SQLStr = SQLStr & "Left JOIN ADB.dbo.Temp_Aims b on b.[charge_time] >= a.[_TimeStamp] and b.[charge_time] < a.[next_time_2] and a.[Furnace] = b.[Furnace]"
    
SQLStr = SQLStr & "WHERE CONVERT(datetime, a.[_TimeStamp], 20)  BETWEEN CONVERT(datetime, @StartDate , 20) AND CONVERT(datetime, @EndDate , 20)"

SQLStr = SQLStr & "Group by a.[_TimeStamp]"
SQLStr = SQLStr & "ORDER BY a.[_TimeStamp]"
      
  SQLStr = SQLStr & "drop table #dischargeeventstable"

SQLStr = SQLStr & "SELECT"
      SQLStr = SQLStr & "[Date1]=  a.[_TimeStamp],"
      SQLStr = SQLStr & "[Discharge_slabs_A]=sum(case when b.[FURNACE] ='A' then 1.0 else '0' end),"
      SQLStr = SQLStr & "[Avg_DisCharg_Temp_A]=avg(CASE WHEN b.[FURNACE] ='A' then convert(real,isnull (b.[ave_disch_temp],'0')) else null end),"
      SQLStr = SQLStr & "[Discharge_slabs_B]=sum(case when b.[FURNACE] ='B' then 1.0 else '0' end),"
      SQLStr = SQLStr & "[Avg_DisCharg_Temp_B]=avg(CASE WHEN b.[FURNACE] ='B' then convert(real,isnull (b.[ave_disch_temp],'0')) else null end)"

SQLStr = SQLStr & "into #dischargeeventstable"

 SQLStr = SQLStr & "FROM (select a.*, (select min(aa.[_TimeStamp])"
            SQLStr = SQLStr & "from ix.dbo.Fce_Data aa"
            SQLStr = SQLStr & "where aa.[_TimeStamp] > a.[_TimeStamp])as next_time_2"
    SQLStr = SQLStr & "from ix.dbo.Fce_Data a) a"
    SQLStr = SQLStr & "Left JOIN ADB.dbo.temp_Aims b on b.[discharge_time] >= a.[_TimeStamp] and b.[discharge_time] < a.[next_time_2] and a.[Furnace] = b.[Furnace]"

SQLStr = SQLStr & "WHERE CONVERT(datetime, a.[_TimeStamp], 20)  BETWEEN CONVERT(datetime, @StartDate , 20) AND CONVERT(datetime, @EndDate , 20)"

SQLStr = SQLStr & "Group by a.[_TimeStamp]"
SQLStr = SQLStr & "ORDER BY a.[_TimeStamp]"
     
      SQLStr = SQLStr & "SELECT"
      
      SQLStr = SQLStr & "[FCE1],"
      SQLStr = SQLStr & "a.[Date1],"
      SQLStr = SQLStr & "[Shift],"
      SQLStr = SQLStr & "[Charge_slabs_A],"
      SQLStr = SQLStr & "[Avg_Charg_Temp_A],"
      SQLStr = SQLStr & "[NGVolA],  "
      SQLStr = SQLStr & "[FCE2],"
      SQLStr = SQLStr & "[Charge_slabs_B],"
      SQLStr = SQLStr & "[Avg_Charg_Temp_B],"
      SQLStr = SQLStr & "[Discharge_slabs_A],"
      SQLStr = SQLStr & "[Discharge_slabs_B]"
       
    SQLStr = SQLStr & "FROM #chargeeventstable a"
    SQLStr = SQLStr & "Left JOIN #dischargeeventstable b on a.[Date1] = b.[Date1]"
    SQLStr = SQLStr & "Left JOIN #slabweightcharged c on a.[Date1]=c.[Date1]"
    SQLStr = SQLStr & "LEFT JOIN #slabweightdischarged d on a.[Date1]=d.[Date1]"

    cn.Open "driver={SQL server}; SERVER=name; UID=user; PWD=pass; DATABASE=;"
    rs.ActiveConnection = cn
    rs.Open SQLStr, cn
    rs.MoveFirst
    i = 20  
    While Not rs.EOF
    
        Sheets("-Input Data-").Cells(i, 2) = rs(0)
        Sheets("-Input Data-").Cells(i, 3) = rs(1)
        
i = i + 1
        rs.MoveNext
    Wend
    rs.Close
    cn.Close

    'Clean up
    Set rs = Nothing
    Set cn = Nothing
    
    Sheets("A&B").Select
    SetScrollBarRange
End Sub
Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable)
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
End Sub

我的第一个问题是如何在 VBA 中从两个 SQL 数据库(来自同一服务器)检索数据。正如您在 VBA 代码中看到的那样,我还没有找到一种方法来做到这一点。第二个问题是如何在 VBA 中处理临时表? QLStr = SQLStr & "into #dischargeeventstable" 是将其转换为 VBA 的正确方法吗? 我将不胜感激任何建议或 cmets。

【问题讨论】:

  • 没有真正看到问题?您的代码是否有效?如果没有,那是什么不工作?
  • @DaleK SQL 代码有效,VBA 没有。我的第一个问题是如何在 VBA 中从两个 SQL 数据库(来自同一服务器)检索数据。正如您在 VBA 代码中看到的那样,我还没有找到一种方法来做到这一点。第二个问题是如何在 VBA 中处理临时表? QLStr = SQLStr & "into #dischargeeventstable" 是将其转换为 VBA 的正确方法吗?
  • edit 你的问题,所以它对人们来说很清楚。
  • VBA 根本不处理临时表,它们只存在于 SQL Server 上。我不希望您在 VBA 中的 SQL 代码能够正常工作,因为代码行之间缺少空格。此外,如果您针对较新版本的 SQL Server 运行此操作,请考虑使用 DROP TABLE IF EXISTS ... 语法或一些变体,否则由于临时表尚不存在,您将收到错误。
  • 你有权限在数据库上创建存储过程吗?

标签: sql-server excel vba


【解决方案1】:

由于您无法创建过程,我已将长 SQL 语句放入函数中以从主 vba 代码中删除它们。我还删除了 ix.dbo 和 ADB.dbo 前缀,因此它在单个数据库上运行。还使用## 将临时表更改为全局表。当脚本运行时,它将 SQL 写入 logfile.txt,以便您可以将语句复制/粘贴到 SQL Management Studio 并调试任何拼写错误。在没有适当数据的情况下,我已经尽可能地测试了脚本。

Option Explicit

Sub click()

    Dim oCon As ADODB.Connection, oCmd As Object
    Dim rs As Object, sSQL As String
    Dim ws As Worksheet, n As Long
    
    ' create log file
    Dim LOGFILE As String
    LOGFILE = ThisWorkbook.Path & "\logfile.txt"
    Dim fs As Object, ts As Object
    Set fs = CreateObject("Scripting.FileSystemObject")
    Set ts = fs.CreateTextFile(LOGFILE, True)
    
    ' get dates
    Dim StartDate As String, EndDate As String
    With ThisWorkbook.Sheets("A&B Sankey")
        StartDate = Format(.Range("R2").Value, "yyyy-mm-dd")
        EndDate = Format(.Range("T2").Value, "yyyy-mm-dd")
    End With
    
    Set oCon = DbConnect ' connect function
    Set oCmd = CreateObject("ADODB.Command")
    oCmd.ActiveConnection = oCon
    ts.writeline "Connected to database: " & oCon.DefaultDatabase
   
    ' create #chargeeventstable
    sSQL = SQL_1(StartDate, EndDate)
    ts.writeline vbCrLf & "--- SQL_1 executing ---" & vbCrLf & sSQL
    
    oCmd.CommandText = sSQL
    oCmd.Execute
    
    ' create #dischargeeventstable
    sSQL = SQL_2(StartDate, EndDate)
    ts.writeline vbCrLf & "--- SQL_2 executing ---" & vbCrLf & sSQL
    
    oCmd.CommandText = sSQL
    oCmd.Execute

    ' SQL_3
    sSQL = " SELECT * " & vbCrLf & _
           " FROM  ##chargeeventstable a" & vbCrLf & _
           " LEFT JOIN ##dischargeeventstable b" & vbCrLf & _
           " ON a.[Date1] = b.[Date1]"
    ts.writeline vbCrLf & "--- SQL_3 executing ---" & vbCrLf & sSQL
           
          '" SELECT [FCE1],a.[Date1],[Shift]," & _
          ' "        [Charge_slabs_A],[Avg_Charg_Temp_A]," & _
          ' "        [NGVolA],[FCE2],[Charge_slabs_B]," & _
          ' "        [Avg_Charg_Temp_B]," & _
          ' "        [Discharge_slabs_A],[Discharge_slabs_B]" & _

    ' execute select
    oCmd.CommandText = sSQL
    Set rs = oCmd.Execute
       
    ' show results
    Set ws = ThisWorkbook.Sheets("-Input Data-")
    ws.Range("B20").CopyFromRecordset rs
    ws.Range("B20").Activate
    
    ' close
    oCon.Close
    ts.Close
    MsgBox "Result written to " & ws.Name & _
           "For " & StartDate & "-" & EndDate, vbInformation, "Finished"

End Sub

Function DbConnect() As ADODB.Connection

    Dim sConn As String
    sConn = "driver={SQL server}; SERVER=name; " & _
            "UID=user; PWD=pass; DATABASE=;"
    
    Set DbConnect = CreateObject("ADODB.Connection")
    DbConnect.Open sConn
    
End Function

Function SQL_1(StartDate As String, EndDate As String) As String

    SQL_1 = _
" DECLARE @StartDate nvarchar(10)" & vbCrLf & _
" DECLARE @EndDate nvarchar(10)" & vbCrLf & _
" SET @StartDate ='" & StartDate & "'" & vbCrLf & _
" SET @EndDate ='" & EndDate & "'" & vbCrLf

SQL_1 = SQL_1 & _
" DROP TABLE IF EXISTS ##chargeeventstable   " & vbCrLf & _
" SELECT " & vbCrLf & _
"   [Date1]= a.[_TimeStamp],[FCE1]='A'," & vbCrLf & _
"   [Shift]= CASE WHEN DATEPART(hour, a.[_TimeStamp]) " & vbCrLf & _
"               BETWEEN 7 AND 18 THEN '1' " & vbCrLf & _
"               ELSE '2' END ," & vbCrLf & _
"   [Avg_Charg_Temp_A]=avg(CASE WHEN a.[FURNACE] ='A' " & vbCrLf & _
"               THEN CONVERT(real,isnull (b.[charge_temperature],'0')) " & vbCrLf & _
"               ELSE null END)," & vbCrLf & _
"   [Charge_slabs_A]=SUM(CASE WHEN a.[FURNACE] ='A' " & vbCrLf & _
"               THEN 1.0 ELSE null END),    " & vbCrLf & _
"   [NGVolA]= SUM(CASE WHEN a.[Furnace] ='A' " & vbCrLf & _
"                THEN CONVERT(real,isnull (a.[NG_AVG_MEAS_FLOW],'0.0')) " & vbCrLf & _
"                ELSE 0.0 END ) ,      " & vbCrLf & _
"   [FCE2]='B' ," & vbCrLf & _
"   [Avg_Charg_Temp_B]=avg(CASE WHEN a.[FURNACE] ='B' " & vbCrLf & _
"                THEN CONVERT(real,isnull (b.[charge_temperature],'0')) " & vbCrLf & _
"                ELSE null END)," & vbCrLf & _
"   [Charge_slabs_B]=SUM(CASE WHEN a.[FURNACE] ='B' " & vbCrLf & _
"                THEN 1.0 " & vbCrLf & _
"                ELSE '0' END) " & vbCrLf & _
" " & vbCrLf & _
" INTO ##chargeeventstable" & vbCrLf

    SQL_1 = SQL_1 & _
" FROM (select a.*, (SELECT min(aa.[_TimeStamp])" & vbCrLf & _
"     FROM dbo.Fce_Data aa " & vbCrLf & _
"     WHERE aa.[_TimeStamp] > a.[_TimeStamp]) AS next_time_2" & vbCrLf & _
"     FROM dbo.Fce_Data a) a " & vbCrLf & _
"     LEFT JOIN dbo.Temp_Aims b " & vbCrLf & _
"         ON b.[charge_time] >= a.[_TimeStamp] " & vbCrLf & _
"        AND b.[charge_time] < a.[next_time_2] " & vbCrLf & _
"        AND a.[Furnace] = b.[Furnace]" & vbCrLf & _
" " & vbCrLf & _
" WHERE CONVERT(datetime, a.[_TimeStamp], 103)  " & vbCrLf & _
"   BETWEEN CONVERT(datetime, @StartDate , 103) " & vbCrLf & _
"   AND CONVERT(datetime, @EndDate , 103) " & vbCrLf & _
" " & vbCrLf & _
" GROUP by a.[_TimeStamp]" & vbCrLf & _
" ORDER BY a.[_TimeStamp]"

End Function

Function SQL_2(StartDate As String, EndDate As String) As String

    SQL_2 = " DECLARE @StartDate nvarchar(10)" & vbCrLf & _
" DECLARE @EndDate nvarchar(10)" & vbCrLf & _
" SET @StartDate ='" & StartDate & "'" & vbCrLf & _
" SET @EndDate ='" & EndDate & "'" & vbCrLf

   SQL_2 = SQL_2 & _
" DROP TABLE IF EXISTS ##dischargeeventstable " & vbCrLf & _
" SELECT [Date1]= a.[_TimeStamp]," & vbCrLf & _
"       [Discharge_slabs_A] = SUM(CASE WHEN b.[FURNACE] ='A' " & vbCrLf & _
"     THEN 1.0 ELSE '0' END)," & vbCrLf & _
"       [Discharge_slabs_B] = SUM(CASE WHEN b.[FURNACE] ='B' " & vbCrLf & _
"     THEN 1.0 ELSE '0' END)     " & vbCrLf & _
" INTO ##dischargeeventstable" & vbCrLf

    SQL_2 = SQL_2 & _
" FROM (SELECT a.*, (SELECT min(aa.[_TimeStamp])" & vbCrLf & _
" FROM dbo.Fce_Data aa " & vbCrLf & _
" WHERE aa.[_TimeStamp] > a.[_TimeStamp]) AS next_time_2" & vbCrLf & _
" FROM dbo.Fce_Data a) a " & vbCrLf & _
" LEFT JOIN dbo.Temp_Aims b " & vbCrLf & _
"     ON b.[discharge_time] >= a.[_TimeStamp] " & vbCrLf & _
"    AND b.[discharge_time] < a.[next_time_2] " & vbCrLf & _
"    AND a.[Furnace] = b.[Furnace]" & vbCrLf & _
" " & vbCrLf & _
" WHERE CONVERT(datetime, a.[_TimeStamp], 103) " & vbCrLf & _
" BETWEEN CONVERT(datetime, @StartDate , 103)" & vbCrLf & _
" AND CONVERT(datetime, @EndDate , 103)     " & vbCrLf & _
" " & vbCrLf & _
" GROUP BY a.[_TimeStamp]" & vbCrLf & _
" ORDER BY a.[_TimeStamp]"

End Function

【讨论】:

  • 非常感谢 CDP1802 我会运行它并让您知道。我有一个愚蠢的问题,虽然在 Dim sConnect 部分您正在设置与特定数据库的连接(比如说 ix.dbo),如何同时从其他数据库(ADB.dbo)获取数据?
  • @theo13 连接将在连接字符串中有一个默认数据库,您可以在没有前缀的情况下使用 SELECT * FROM [table] 但可以使用完全限定名称访问或连接任何其他数据库,例如SELECT * FROM [anotherdb].[dbo].[table],当然前提是你有权限。
  • 非常感谢,你的方法正是我所需要的。
猜你喜欢
  • 2015-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-17
  • 1970-01-01
相关资源
最近更新 更多