我正在尝试列出服务器的数据库和表。
我试过像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,并且可以与OrderHeaderEntity
,OrderDetailEntity
,CustomerEntity
,SalesRepEntity
以及其他任何对象一起使用类组成您的领域。是的,OOP需要更多的工作和更多的模块。但是结果是,您现在正在寻找职责明确的模块,一个位置使用SQL Server语法,另一个位置使用MySQL语法,而另一个模块使用Oracle语法-无需任何切换逻辑。为了扩展它并支持新的RDBMS,您甚至不需要接触现有的代码-您只需实现所需的类,以便代码可以与新的RDBMS一起使用,然后在入口点将它们全部连接起来:一旦完成,交换RDBMS所需要做的就是更改要实例化的具体类,并将参数注入到入口点的使用代码中。
关于mysql - 列出VBA中服务器的数据库和表,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57166622/