是否有人知道是否有方法使用Powershell获取ODBC连接的架构数据?我正在使用一个旧的数据库管理系统,没有什么比"Information_Schema“更好了。我希望能够获得一个表的列表及其所有相关的列、数据类型等。
发布于 2022-01-11 16:23:09
您可能需要调整连接字符串和对象结构以适应特定的DSN,但这是它的要点。
function Get-ODBCSchema{
param(
[string]
[ValidateNotNullOrEmpty()]
$DataSourceName
)
try{$dsn = get-odbcdsn $DataSourceName -ErrorAction Stop}catch {
throw $_.Exception
}
if($dsn.Platform -NotMatch ([IntPtr]::Size*8))
{throw "You're currently in $([IntPtr]::Size*8)-Bit PowerShell, and the ODBC is only available in $($dsn.Platform)"}
$con = [System.Data.Odbc.OdbcConnection]::new()
$con.ConnectionString = 'DSN={0}' -f $DataSourceName
$con.open()
write-host "Downloading Column Data from $DataSourceName, this may take some time"
$columns = $con.GetSchema('Columns')
write-host 'got it'
$con.close();$con.dispose()
$count = $columns.Rows.Count
[int]$aPercent = $count / 100
$ret = @{}; $i = $j = 0
$columns.Rows.GetEnumerator().foreach{
$qual = $_.TABLE_QUALIFIER.ToString()
$owner = $_.TABLE_OWNER.ToString()
$tName = $_.TABLE_NAME.ToString()
$cName = $_.COLUMN_NAME.ToString()
if($ret.Keys -NotContains $qual)
{$ret.$qual = @{$owner = @{$tName = @{$cName = $_}}}}
elseif($ret.$qual.Keys -NotContains $owner)
{$ret.$qual.$owner = @{$tName = @{$cName = $_}}}
elseif($ret.$qual.$owner.Keys -NotContains $tName)
{$ret.$qual.$owner.$tName = @{$cName = $_}}
else
{$ret.$qual.$owner.$tName.$cName = $_}
if(!($i++ % $aPercent)){
write-progress -Activity 'Building Schema Table' -Status ('Added Column: {0:#,##0} of {1:#,##0}' -f $i,$count) -PercentComplete ($j++)
}
}
write-progress -Activity 'Building Schema Table' -Completed
if($ret.Keys.Count -eq 1 -And $ret.Keys[0].Length -eq 0)
{$ret = $ret.Values[0]}
return $ret
}Edit1:找出需要微调的内容
打开PowerShell
PS C:\working> 'This PowerShell session is running in {0}-bit mode' -f ([IntPtr]::Size*8)
This PowerShell session is running in 64-bit mode
PS C:\working> Get-OdbcDsn 'Excel Files'
Name : Excel Files
DsnType : User
Platform : 32-bit
DriverName : Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)
Attribute : {SafeTransactions, DriverId, ImplicitCommitSync, Threads...}Dang,DSN平台只在32位模式下工作,但是这个PS是64位的.所以我想我们需要启动一个新的32位会话
PS C:\working> start-process $env:SystemRoot\syswow64\windowspowershell\v1.0\powershell.exe好吧,现在我们应该开始挖掘了
PS C:\working> 'This PowerShell session is running in {0}-bit mode' -f ([IntPtr]::Size*8)
This PowerShell session is running in 32-bit mode
PS C:\working> $con = [System.Data.Odbc.OdbcConnection]::new()
PS C:\working> $con.ConnectionString = 'DSN=Excel Files'
PS C:\working> $con.Open()
Exception calling "Open" with "0" argument(s): "ERROR [IM006] [Microsoft][ODBC Driver Manager] Driver's
SQLSetConnectAttr failed"
At line:1 char:1
+ $con.Open()
+ ~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : OdbcException好吧,也许需要一个文件名?
PS C:\working> $con.ConnectionString = 'DSN=Excel Files;File Name={0}' -f (ls '.\Book1.xlsx').FullName
PS C:\working> $con.Open()
Exception calling "Open" with "0" argument(s): "ERROR [IM006] [Microsoft][ODBC Driver Manager] Driver's
SQLSetConnectAttr failed
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute File Name
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute File Name
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute File Name
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute File Name
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute File Name
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute File Name
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute File Name
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute File Name"
At line:1 char:1
+ $con.Open()
+ ~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : OdbcException不同的错误,希望朝着正确的方向前进,现在它在抱怨attribute File Name
PS C:\working> $con.ConnectionString = 'DSN=Excel Files;Data Source={0}' -f (ls '.\Book1.xlsx').FullName
PS C:\working> $con.Open()
Exception calling "Open" with "0" argument(s): "ERROR [IM006] [Microsoft][ODBC Driver Manager] Driver's
SQLSetConnectAttr failed
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute Data Source
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute Data Source
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute Data Source
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute Data Source
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute Data Source
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute Data Source
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute Data Source
ERROR [01S00] [Microsoft][ODBC Excel Driver]Invalid connection string attribute Data Source"
At line:1 char:1
+ $con.Open()
+ ~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : OdbcException愚蠢的运气到此为止;显然,这也不是attribute Data Source。也许我该去找出什么Excel的神奇词汇是?看起来可能是Dbq;因为.擅长吗?
PS C:\working> $con.ConnectionString = 'DSN=Excel Files;Dbq={0}' -f (ls '.\Book1.xlsx').FullName
PS C:\working> $con.Open()
PS C:\working>就像这样,我们已经踏足了大门,让我们看看能找到什么。
PS C:\working> $con.GetSchema()
CollectionName NumberOfRestrictions NumberOfIdentifierParts
-------------- -------------------- -----------------------
MetaDataCollections 0 0
DataSourceInformation 0 0
DataTypes 0 0
Restrictions 0 0
ReservedWords 0 0
Columns 4 4
Indexes 4 4
Tables 3 3
Views 3 3
PS C:\working> $tableinfo = $con.GetSchema('Tables')
PS C:\working> $enu = $tableinfo.Rows.GetEnumerator()
PS C:\working> $enu.MoveNext()
True
PS C:\working> $enu.Current
TABLE_CAT : C:\working\Book1.xlsx
TABLE_SCHEM :
TABLE_NAME : Sheet1$
TABLE_TYPE : SYSTEM TABLE
REMARKS :
PS C:\working> $columninfo = $con.GetSchema('Columns')
PS C:\working> $enu = $columninfo.Rows.GetEnumerator()
PS C:\working> $enu.MoveNext();$enu.Current
True
TABLE_CAT : C:\working\Book1.xlsx
TABLE_SCHEM :
TABLE_NAME : Sheet1$
COLUMN_NAME : Payer No
DATA_TYPE : 8
TYPE_NAME : NUMBER
COLUMN_SIZE : 53
BUFFER_LENGTH : 8
DECIMAL_DIGITS :
NUM_PREC_RADIX : 2
NULLABLE : 1
REMARKS :
COLUMN_DEF :
SQL_DATA_TYPE : 8
SQL_DATETIME_SUB :
CHAR_OCTET_LENGTH :
ORDINAL_POSITION : 1
IS_NULLABLE : YES
ORDINAL : 1
PS C:\working> $con.Close();$con.Dispose();remove-variable con,enu;[gc]::Collect()
PS C:\working>嗯,看来我需要对Get-ODBCSchema做一些调整,以便它可以使用这个数据源;即:TABLE_QUALIFIER到TABLE_CAT,TABLE_OWNER到TABLE_SCHEM,并添加对文件名的支持。
PS C:\working> function Get-ODBCSchema{
>> param(
>> [string]
>> [ValidateNotNullOrEmpty()]
>> $DataSourceName
>> ,[string][ValidateNotNullOrEmpty()]$FullFileName
>> )
>> if(!(test-path $FullFileName -type leaf)) >> {throw "Missing file: $FullFileName"} >> try{$dsn = get-odbcdsn $DataSourceName -ErrorAction Stop}catch { >> throw $_.Exception >> } >> if($dsn.Platform -NotMatch ([IntPtr]::Size*8))
>> {throw "You're currently in $([IntPtr]::Size*8)-Bit PowerShell, and the ODBC is only available in $($dsn.Platform)"}
>>
>> $con = [System.Data.Odbc.OdbcConnection]::new()
>> $con.ConnectionString = 'DSN={0};Dbq={1}' -f $DataSourceName,$FullFileName
>> $con.open()
>> write-host "Downloading Column Data from $DataSourceName, this may take some time"
>> $columns = $con.GetSchema('Columns')
>> write-host 'got it'
>> $con.close();$con.dispose()
>> $count = $columns.Rows.Count
>> [int]$aPercent = $count / 100
>>
>> $ret = @{}; $i = $j = 0
>> $columns.Rows.GetEnumerator().foreach{
>> $qual = $_.TABLE_CAT.ToString()
>> $owner = $_.TABLE_SCHEM.ToString()
>> $tName = $_.TABLE_NAME.ToString()
>> $cName = $_.COLUMN_NAME.ToString()
>>
>> if($ret.Keys -NotContains $qual)
>> {$ret.$qual = @{$owner = @{$tName = @{$cName = $_}}}}
>> elseif($ret.$qual.Keys -NotContains $owner)
>> {$ret.$qual.$owner = @{$tName = @{$cName = $_}}}
>> elseif($ret.$qual.$owner.Keys -NotContains $tName)
>> {$ret.$qual.$owner.$tName = @{$cName = $_}}
>> else
>> {$ret.$qual.$owner.$tName.$cName = $_}
>> if(!($i++ % $aPercent)){
>> write-progress -Activity 'Building Schema Table' -Status ('Added Column: {0:#,##0} of {1:#,##0}' -f $i,$count) -PercentComplete ($j++)
>> }
>> }
>> write-progress -Activity 'Building Schema Table' -Completed
>> if($ret.Keys.Count -eq 1 -And $ret.Keys[0].Length -eq 0)
>> {$ret = $ret.Values[0]}
>> return $ret
>> }
PS C:\working> $s = Get-ODBCSchema 'Excel Files' (ls .\Book1.xlsx).FullName
Downloading Column Data from Excel Files, this may take some time
got it
PS C:\working> $s
Name Value
---- -----
C:\working\Book1.xlsx {}
PS C:\working> $s.Values
Name Value
---- -----
{Sheet3$, Sheet1$, Sheet2$, Sheet2$_xlnm#_FilterDatabase...}
PS C:\working> $s.Keys
C:\working\Book1.xlsx
PS C:\working> [string]$k1 = $s.keys[0]
PS C:\working> $s.$k1
Name Value
---- -----
{Sheet3$, Sheet1$, Sheet2$, Sheet2$_xlnm#_FilterDatabase...}
PS C:\working> $s.$k1.values
Name Value
---- -----
Sheet3$ {F1}
Sheet1$ {Due Date, Invoice No, REC_STAT2, Comp Invoice Date...}
Sheet2$ {FileName, EqLoc City, Invoice No, Sub...}
Sheet2$_xlnm#_FilterDatabase {FileName, EqLoc City, Invoice No, Sub...}
Sheet1$_xlnm#_FilterDatabase {Due Date, Invoice No, REC_STAT2, Comp Invoice Date...}
PS C:\working> $s.$k1.values[0]
Name Value
---- -----
Sheet3$ {F1}
Sheet1$ {Due Date, Invoice No, REC_STAT2, Comp Invoice Date...}
Sheet2$ {FileName, EqLoc City, Invoice No, Sub...}
Sheet2$_xlnm#_FilterDatabase {FileName, EqLoc City, Invoice No, Sub...}
Sheet1$_xlnm#_FilterDatabase {Due Date, Invoice No, REC_STAT2, Comp Invoice Date...}
PS C:\working> $s.$k1 = $s.$k1.values[0]
PS C:\working> $s
Name Value
---- -----
C:\working\Book1.xlsx {System.Collections.Hashtable}
PS C:\working> $s.$k1
Name Value
---- -----
Sheet3$ {F1}
Sheet1$ {Due Date, Invoice No, REC_STAT2, Comp Invoice Date...}
Sheet2$ {FileName, EqLoc City, Invoice No, Sub...}
Sheet2$_xlnm#_FilterDatabase {FileName, EqLoc City, Invoice No, Sub...}
Sheet1$_xlnm#_FilterDatabase {Due Date, Invoice No, REC_STAT2, Comp Invoice Date...}
PS C:\working> $s.$k1.'Sheet1$'
Name Value
---- -----
Due Date System.Data.DataRow
Invoice No System.Data.DataRow
REC_STAT2 System.Data.DataRow
Comp Invoice Date System.Data.DataRow
Equip Loc Street 1 System.Data.DataRow
T...所以,我想这或多或少是可行的,唯一剩下的就是清理掉在头部折叠空分支的函数末尾的位,然后将第一个子部分折叠起来。
function Get-ODBCSchema{
param(
[string]
[ValidateNotNullOrEmpty()]
$DataSourceName
,[string][ValidateNotNullOrEmpty()]$FullFileName
)
if(!(test-path $FullFileName -type leaf))
{throw "Missing file: $FullFileName"}
try{$dsn = get-odbcdsn $DataSourceName -ErrorAction Stop}catch {
throw $_.Exception
}
if($dsn.Platform -NotMatch ([IntPtr]::Size*8))
{throw "You're currently in $([IntPtr]::Size*8)-Bit PowerShell, and the ODBC is only available in $($dsn.Platform)"}
$con = [System.Data.Odbc.OdbcConnection]::new()
$con.ConnectionString = 'DSN={0};Dbq={1}' -f $DataSourceName,$FullFileName
$con.open()
write-host "Downloading Column Data from $DataSourceName, this may take some time"
$columns = $con.GetSchema('Columns')
write-host 'got it'
$con.close();$con.dispose()
$count = $columns.Rows.Count
[int]$aPercent = $count / 100
$ret = @{}; $i = $j = 0
$columns.Rows.GetEnumerator().foreach{
$qual = $_.TABLE_CAT.ToString()
$owner = $_.TABLE_SCHEM.ToString()
$tName = $_.TABLE_NAME.ToString()
$cName = $_.COLUMN_NAME.ToString()
if($ret.Keys -NotContains $qual)
{$ret.$qual = @{$owner = @{$tName = @{$cName = $_}}}}
elseif($ret.$qual.Keys -NotContains $owner)
{$ret.$qual.$owner = @{$tName = @{$cName = $_}}}
elseif($ret.$qual.$owner.Keys -NotContains $tName)
{$ret.$qual.$owner.$tName = @{$cName = $_}}
else
{$ret.$qual.$owner.$tName.$cName = $_}
if(!($i++ % $aPercent)){
write-progress -Activity 'Building Schema Table' -Status ('Added Column: {0:#,##0} of {1:#,##0}' -f $i,$count) -PercentComplete ($j++)
}
}
write-progress -Activity 'Building Schema Table' -Completed
[string]$k1 = $ret.Keys[0]
if($ret.$k1.Keys.Count -eq 1 -And $ret.$k1.Keys[0].Length -eq 0)
{$ret.$k1 = $ret.$k1.Values[0]}
return $ret
}现在我应该有一个适用于Excel Files dsn的版本。
$schema = Get-ODBCSchema 'Excel Files' (ls .\Book1.xlsx).FullNameEdit2:用它做点什么
既然我们在导出模式时遇到了很多麻烦,那么不妨用它制作一些表.
PS C:\working> $WorkBook = ls .\Book1.xlsx
PS C:\working> $schema = Get-ODBCSchema 'Excel Files' $WorkBook.FullName
Downloading Column Data from Excel Files, this may take some time
got it
PS C:\working> function Get-TypeString{
>> param(
>> [Parameter(ValueFromPipelineByPropertyName=$true)]$COLUMN_NAME
>> ,[Parameter(ValueFromPipelineByPropertyName=$true)]$TYPE_NAME
>> ,$TabWidth = 30
>> )process{
>> $out = switch($TYPE_NAME){
>> 'VARCHAR' {" ,{0,-$TabWidth} varchar(255)";break}
>> 'NUMBER' {" ,{0,-$TabWidth} decimal(18,5)";break}
>> 'DATETIME'{" ,{0,-$TabWidth} datetime";break}
>> default {" ,{0,-$TabWidth} varchar(255)";break}
>> }
>> $out -f "[$COLUMN_NAME]"
>> }}
PS C:\working> $DBSchema = 'GreenField.dbo.'
PS C:\working> $sql = [System.Text.StringBuilder]::new(@"
>> /*
>> * Infrastructure as Code
>> * SQL Server - Data Description Language
>> * Auto Generated From:
>> * $($WorkBook.FullName)
>> */
>> "@)
PS C:\working> [string]$wb = $schema.Keys[0]
PS C:\working> [string[]]$tables = $schema.$wb.Keys | sort-object
PS C:\working> $tables.foreach{
>> $table = $_
>> if($schema.$wb.$table.Keys.Count -eq 0)
>> {continue}
>> $fields = $schema.$wb.$table.GetEnumerator() | sort-object -Property @{Expression={$_.Value.ORDINAL};Ascending=$true}
>> if($fields.Count -eq 1)
>> {$fields = @($fields)}
>> $enu = $fields.GetEnumerator()
>> $null = $enu.MoveNext()
>> $null = $sql.Append("`r`n`r`n")
>> $null = $sql.Append(((@'
>> if OBJECT_ID('{0}[{1}]') is not null
>> drop table {0}[{1}];
>> create table {0}[{1}](
>> '@ -f $DBSchema,$table) -replace '(^|\n)\s{6}','$1'))
>> $null = $sql.Append("`r`n")
>> $null = $sql.Append((($enu.Current.Value|Get-TypeString) -replace '^(\s+),','$1 '))
>> $enu.foreach{
>> $null = $sql.Append("`r`n")
>> $null = $sql.Append(($_.Value|Get-TypeString))
>> }
>> $null = $sql.Append("`r`n);")
>> }
PS C:\working> $SQLFile = '{0}-IaC-DDL.sqlserver.sql' -f $WorkBook.Name.Replace($WorkBook.Extension,'')
PS C:\working> new-item $SQLFile -Value $sql.ToString() -Force
Directory: C:\working
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 1/13/2022 5:32 PM 14908 Book1-IaC-DDL.sqlserver.sql
PS C:\working> gc .\Book1-IaC-DDL.sqlserver.sql
/*
* Infrastructure as Code
* SQL Server - Data Description Language
* Auto Generated From:
* C:\working\Book1.xlsx
*/
if OBJECT_ID('GreenField.dbo.[Sheet1$]') is not null
drop table GreenField.dbo.[Sheet1$];
create table GreenField.dbo.[Sheet1$](
[Payer No] decimal(18,5)
,[Composite Invoice] decimal(18,5)
,[Comp Invoice Date] datetime
...
if OBJECT_ID('GreenField.dbo.[Sheet3$]') is not null
drop table GreenField.dbo.[Sheet3$];
create table GreenField.dbo.[Sheet3$](
[F1] varchar(255)
);
PS C:\working>https://stackoverflow.com/questions/70660296
复制相似问题