我正在尝试列出服务器的数据库和表。

我试过像OpenSchema中那样使用this example,但是这样做时返回null:

Set Recordset1 = Connection.OpenSchema(Schema:=ADODB.SchemaEnum.adSchemaSchemata)
MsgBox Prompt:=VBA.IsNull(Expression:=Recordset1.Fields.Item(Index:=0))


我下面有一些适用于MySQL服务器的代码,但我想知道一种更标准且不特定于一个DBMS的方法。

' Reference:
' Microsoft ActiveX Data Objects 6.1 Library

Private Sub Macro()

  Dim Connection As ADODB.Connection
  Dim Recordset1 As ADODB.Recordset
  Dim Recordset2 As ADODB.Recordset

  Set Connection = New ADODB.Connection
  Connection.ConnectionString = "Driver={MySQL ODBC 5.1 Driver};Server=127.0.0.1;Port=3306"
  Connection.ConnectionTimeout = 0
  Connection.Open UserId:="root", Password:=""
  Set Recordset1 = New ADODB.Recordset
  Recordset1.ActiveConnection = Connection
  Recordset1.CursorLocation = ADODB.CursorLocationEnum.adUseClient
  Recordset1.Source = "SHOW DATABASES"
  Recordset1.Open
  Set Recordset2 = New ADODB.Recordset
  Recordset2.ActiveConnection = Connection
  Recordset2.CursorLocation = ADODB.CursorLocationEnum.adUseClient
  Do While Not Recordset1.EOF
    Connection.Execute "USE " & Recordset1.Fields.Item(Index:="Database").Value
    Recordset2.Source = "SHOW TABLES"
    Recordset2.Open
    Do While Not Recordset2.EOF
      Debug.Print Recordset1.Fields.Item(Index:=0).Value & " - " & Recordset2.Fields.Item(Index:=0).Value
      Recordset2.MoveNext
    Loop
    Recordset2.Close
    Recordset1.MoveNext
  Loop
  Set Recordset2 = Nothing
  Recordset1.Close
  Set Recordset1 = Nothing
  Connection.Close
  Set Connection = Nothing

End Sub

最佳答案

每个RDBMS都有自己的SQL风格。 SQL Server支持表值函数和公用表表达式(CTE),而MySQL不但具有整洁的group_concat函数。 Oracle具有不同的语法,Access和DB2也都有其自身的局限性和特殊性-如果您可以编写一个适用于每个RDBMS的SQL查询,您将非常幸运。

使用过程方法,您需要一种让代码知道使用哪种SQL风格的方法。您可以使用Enum



Public Enum RDBMS
    NotSupported
    SqlServer
    MySql
    Oracle
End Enum


然后,您将具有切换逻辑以获取所需做的正确的SQL语法:

Private Function GetTableInfoQuery(ByVal db As RDBMS) As String
    Select Case db
        Case SqlServer
            GetTableInfoQuery = "SELECT * FROM sys.Tables;"
        Case MySql
            GetTableInfoQuery = "MySQL-specific query;"
        Case Oracle
            GetTableInfoQuery = "Oracle-specific query;"
        Case Else
            Err.Raise 5, TypeName(Me), "Specified RDBMS is not supported"
     End Select
End Function


这将起作用,但很快就会变得烦人,而且您现在拥有包含不同风格的SQL语法的更多函数(并且对于不同的连接字符串而言,更多的功能),并保持最终成为噩梦-更不用说调试它了。

问题不在于支持多个RDBMS,而在于程序方法。面向对象编程(OOP)带来了更简洁的语言。在任何典型的C#或Java数据访问代码中,RDBMS都是使用接口从使用方代码中抽象出来的-“模式”称为“存储库”,其主要优点是使用方代码根本不在乎什么正在使用的RDBMS。

定义一个暴露所需功能的接口:

'@Interface "ITableInfoRepository"
Option Explicit

Public Function GetTableInfo() As Object
End Function


通常,更具通用性的存储库界面可能如下所示:

Public Function GetAll() As Object
End Function

Public Function GetById(ByVal id As Long) As Object
End Function

Public Sub Delete(ByVal id As Long)
End Sub

Public Sub Save(ByVal entity As Object)
End Sub


在支持泛型的语言中,您不需要As Object并且可以保留类型安全性-但由于VBA不支持泛型(例如VB.NET中的List(Of Something)),因此Object就足够了。

因此,想法是针对该IRepository接口进行编码:

Public Sub DoSomething(ByVal repository As ITableInfoRepository)
    Dim infos As Object
    Set infos = repository.GetTableInfo
    '...do stuff...
End Sub


请注意,DoSomething如何仅了解ITableInfoRepository抽象接口,而不关心它是否真正影响了SQL Server,MySQL或Oracle。在代码/宏的入口点附近(或附近),您将创建实际具体类型的实例:

Public Sub Macro1()
    Dim repository As ITableInfoRepository
    Set repository = New SqlTableInfoRepository
    DoSomething repository
    '...
End Sub


如果Macro1突然需要解决MySqlTableInfoRepository,则在新的ITableInfoRepository类模块上实现MySqlTableInfoRepository接口,并实例化该类:DoSomething不会引起您的注意。

您可以使用Implements关键字实现接口。例如,SqlTableInfoRepository实现可能如下所示:

Option Explicit
Implements ITableInfoRepository

Private Function ITableInfoRepository_GetTableInfo() As Object
    Const sql = "SELECT * FROM sys.Tables"
    Dim conn As ADODB.Connection
    '...
End Function


您可以使GetTableInfo返回ADODB.Recordset,但是不同的实现之间的列名可能不同,因此您不希望这样做。因此,要么确保所有实现SELECT都位于同一列,要么将查询结果抽象到一个实体对象之后,该实体对象将“表信息”记录的形式形式化,并返回这些实体的集合:

'TableInfoEntity (class)
Option Explicit
Public DatabaseName As String
Public TableName As String 'SQL Server: includes 'dbo' schema
Public ColumnName As String
Public DataType As String
'...


同样,如果您有一个SqlOrderHeaderRepository,则可能有一个OrderHeaderEntity类模块,以便所有处理订单标头的IRepository实现都具有与订单标头相同的概念:

Option Explicit
Public OrderDate As Date
Public CustomerID As Long
Public SalesRepID As Long
Public Description As String
'...


这样,DoSomething(或使用IRepository接口的任何代码)甚至不需要了解ADODB,并且可以与OrderHeaderEntityOrderDetailEntityCustomerEntitySalesRepEntity以及其他任何对象一起使用类组成您的领域。

是的,OOP需要更多的工作和更多的模块。但是结果是,您现在正在寻找职责明确的模块,一个位置使用SQL Server语法,另一个位置使用MySQL语法,而另一个模块使用Oracle语法-无需任何切换逻辑。为了扩展它并支持新的RDBMS,您甚至不需要接触现有的代码-您只需实现所需的类,以便代码可以与新的RDBMS一起使用,然后在入口点将它们全部连接起来:一旦完成,交换RDBMS所需要做的就是更改要实例化的具体类,并将参数注入到入口点的使用代码中。

关于mysql - 列出VBA中服务器的数据库和表,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57166622/

10-09 08:35