0

我在 sql server 中有一个 100 列的表,我想制作它,所以不是所有的列都需要在文件中传递才能加载。我在一个表中分配了列名,然后比较哈希表中的列以找到匹配的列。然后,我根据要用于从文件中插入数据的数组的匹配来创建代码。问题是,它不喜欢调用一个变量来创建自定义对象。

我将以下内容存储在一个数组中。(最多 100 个,示例如下(注意 sqlcolumn2 被跳过))。

  • sqlcolumn1 =if ([string]::IsNullOrEmpty($obj.P1) -eq $true) {$null} else {"$obj.P1"}
  • sqlcolumn3 =if ([string]::IsNullOrEmpty($obj.P2) -eq $true) {$null} else {"$obj.P2"}
  • sqlcolumn4 =if ([string]::IsNullOrEmpty($obj.P3) -eq $true) {$null} else {"$obj.P3"}
  • sqlcolumn5 =if ([string]::IsNullOrEmpty($obj.P4) -eq $true) {$null} else {"$obj.P4"}

这是数组:

foreach($line in $Final)
{
    $DataRow = "$($line."TableColumnName") = if ([string]::IsNullOrEmpty(`$obj.$($line."PName")) -eq `$true) {`$null} else {`"`$obj.$($line."PName")`"}"
    $DataArray += $DataRow
}

然后我尝试将它添加到最终数组中,我希望在其中循环遍历每一行数据,之后我将从数组中执行插入。即使上面数组中的“字符串”值是正确的,如果它是手动编码的,我也无法让它识别行并运行。

foreach ($obj in $data2)
{
   $test = [PSCustomObject] @{  
   $DataArray = Invoke-Expression $DataArray
}

如果我只输入$DataArray,它不喜欢这样,因为它需要我已经在字符串中内置的 = 符号。

是我想要做的甚至可能。

我试图模板化我们接收这些数据的各种不同方式,其中有些人向我们发送了 100 列中的 30 列,其他人或多或少,没有人使用确切的列来减少所有内容的单个脚本。

添加更多代码:

Function ArrayCompare() {
 [CmdletBinding()]
 PARAM(
    [Parameter(Mandatory=$True)]$Array1,
    [Parameter(Mandatory=$True)]$A1Match,
    [Parameter(Mandatory=$True)]$Array2,
    [Parameter(Mandatory=$True)]$A2Match)

 $Hash = @{}
 foreach ($Data In $Array1) {
    $Hash[$Data.$A1Match] += ,$Data
 }
 foreach ($Data In $Array2) {
    $Hash[$Data.$A2Match] += ,$Data
 }
 foreach ($KeyValue In $Hash.GetEnumerator()){

    $Match1, $Match2 = $KeyValue.Value.Where( {$_.$A1Match}, 'Split')

        [PSCustomObject]@{
        MatchValue = $KeyValue.Key
        A1Matches = $Match1.Count
        A2Matches = $Match2.Count
        TablePosition = [int]$Match2.TablePosition
        TableColumnName = $Match2.TableColumnName
        #  PName is the P(##) that is a generic ascending column value back to import-excel module.  ColumnA = P1, ColumnB = P2  etc..until no data is detected.  Allows flexibility and not having to know how many columns there are
        PName = $Match1.Name}  
 }
}


$Server = 'ServerName'
$Catalog = 'DBName'
$DestinationTable = 'ImportIntoTableName'

$FileIdentifierID = 10
$FileName = 'Test.xlsx'
$FilePath = 'C:\'

$FullFilePath =  $FilePath + $FileName
$data = Import-Excel -Path $FullFilePath -NoHeader -StartRow 1 # Import- 
Excel Module for working with xlsx excel files
$data2 = Import-Excel -Path $ullFilePath -NoHeader -StartRow 2 # Import- 
Excel Module for working with xlsx excel files

$ExpectedHeaderArray = @()
$HeaderArray = @()
$DataArray = @()
$HeaderDetect = @()

$HeaderDetect = $data | Select-Object -First 1 # Header Row In File

$HeaderDetect | 
ForEach-Object  {
                 $ColumnValue = $_
                 $ColumnValue | 
                 Get-Member -MemberType *Property |
                 Select-Object -ExpandProperty Name |
                 ForEach-Object  {
                                    $HeaderValues = [PSCustomObject]@{
                                    Name = $_
                                    Value = $ColumnValue.$_}
                                    $HeaderArray += $HeaderValues
                                 }
                 } 

# Query below provides a list of all expected file headers and the table 
column name they map to
$Query = "SELECT TableColumnName, FileHeaderName, TablePosition FROM 
dbo.FileHeaders WHERE FileIdentifierID = $($FileIdentifierID)" 
$ds = Invoke-Sqlcmd -ServerInstance $Server -Database $Catalog -Query $Query 
-OutputAs DataSet

$ExpectedHeaderArray =  foreach($Row in $ds.Tables[0].Rows)
    {
    new-object psObject -Property @{ 
        TableColumnName = "$($row.TableColumnName)"
        FileHeaderName = "$($row.FileHeaderName)"
        TablePosition = "$($row.TablePosition)"
    }
}

#Use Function Above
#Bring it together so we know what P(##) goes with which header in file/mapped to table column name
$Result = ArrayCompare -Array1 $HeaderArray -A1Match Value -Array2 $ExpectedHeaderArray -A2Match FileHeaderName  

$Final = $Result | sort TablePosition

foreach($Line in $Final)
{
    $DataRow = "$($Line."TableColumnName") = if ([string]::IsNullOrEmpty(`$obj.$($Line."PName")) -eq `$true) {`$null} else {`"`$obj.$($Line."PName"))`"}"
    $DataArray += $DataRow
}

# The output below is what the code inside the last array would be that I would use to import into excel. 
# The goal is to be dynamic and match headers in the file to the stored header value and import into a table (mapped from header column to table column name)
# The reason for this is before I was here, there were many different "versions" of a layout that was given out.  In the end, it is all one in the same
#    but some send all 100 columns, some only send a handful, some send 80 etc.  I am trying to have everything flow through here vs. 60+ pieces of code/stored procedures/ssis packs


 Write-Output $DataArray    

# Output Sample  -- Note how in the sample, P2 and subsequent skip SQLColumn2 because P2 maps to the header value of position 3 in the sql table and each after is one off.  
# In this example, SqlColumn2 would not be populated

# SqlColumn1 = if ([string]::IsNullOrEmpty($obj.P1) -eq $true) {$null} else {"$obj.P1"}
# SqlColumn3 = if ([string]::IsNullOrEmpty($obj.P2) -eq $true) {$null} else {"$obj.P2"}
# SqlColumn4 = if ([string]::IsNullOrEmpty($obj.P3) -eq $true) {$null} else {"$obj.P3"}
# SqlColumn5 = if ([string]::IsNullOrEmpty($obj.P4) -eq $true) {$null} else {"$obj.P4"}


# I know this doesn't work.  This is where I'm stuck, how to build an array now off of this output from above
foreach ($obj in $data2)
{
   $test = [PSCustomObject] @{  
   $DataArray = Invoke-Expression $DataArray}
}
4

1 回答 1

1

我将首先重新陈述您的问题,以确保我正确理解它(我可能没有!)...

  • 你有一个看起来像这样的 excel 文件:
+---+---------+---------+---------+
|   |    A    |    B    |    C    |
+---+---------+---------+---------+
| 1 | HeaderA | HeaderB | HeaderC |
+---+---------+---------+---------+
| 2 | Value P | Value Q | Value R |
+---+---------+---------+---------+
| 3 | Value S | Value T | Value U |
+---+---------+---------+---------+
  • 您还有一个如下所示的数据库表:
+---------+---------+---------+---------+
+ ColumnW | ColumnX | ColumnY | ColumnZ |
+---------+---------+---------+---------+
+ ....... | ....... | ....... | ....... |
+---------+---------+---------+---------+
  • 和这样的列映射表(注意,ColumnX在此示例中未映射):
+-----------------+----------------+---------------+
| TableColumnName | FileHeaderName | TablePosition |
+-----------------+----------------+---------------+
|    ColumnW      |    HeaderA     |       1       |
+-----------------+----------------+---------------+
|    ColumnY      |    HeaderB     |       2       |
+-----------------+----------------+---------------+
|    ColumnZ      |    HeaderC     |       3       |
+-----------------+----------------+---------------+
  • 您想使用映射表中的数据将电子表格中的值插入到数据库表中,以便获得以下信息:
+---------+---------+---------+---------+
+ ColumnW | ColumnX | ColumnY | ColumnZ |
+---------+---------+---------+---------+
+ Value P |   null  | Value Q | Value R |
+---------+---------+---------+---------+
+ Value S |   null  | Value T | Value U |
+---------+---------+---------+---------+

所以让我们加载电子表格(这次让标题行生成有意义的属性名称):

$data = Import-Excel -Path ".\MySpreadsheet.xlsx";
write-host ($data | ft | out-string);

# HeaderA HeaderB HeaderC
# ------- ------- -------
# Value P Value Q Value R
# Value S Value T Value U

并获取您的列映射数据(我正在以编程方式创建一个内存数据集,但您显然是从数据库中读取数据):

$mappings = new-object System.Data.DataTable;
$null = $mappings.Columns.Add("TableColumnName", [string]);
$null = $mappings.Columns.Add("FileHeaderName", [string]);
$null = $mappings.Columns.Add("TablePosition", [int]);
@(
    @{ "TableColumnName"="ColumnW"; "FileHeaderName"="HeaderA"; "TablePosition"=1 },
    @{ "TableColumnName"="ColumnY"; "FileHeaderName"="HeaderB"; "TablePosition"=2 },
    @{ "TableColumnName"="ColumnZ"; "FileHeaderName"="HeaderC"; "TablePosition"=3 }
) | % {
    $row = $mappings.NewRow();
    $row.TableColumnName = $_.TableColumnName;
    $row.FileHeaderName = $_.FileHeaderName;
    $row.TablePosition = $_.TablePosition;
    $mappings.Rows.Add($row);
}
$ds = new-object System.Data.DataSet;
$ds.Tables.Add($mappings);
write-host ($ds.Tables[0] | ft | out-string)

# TableColumnName FileHeaderName TablePosition
# --------------- -------------- -------------
# ColumnW         HeaderA                    1
# ColumnY         HeaderB                    2
# ColumnZ         HeaderC                    3

现在我们可以构建“映射”对象:

$values = @();
foreach( $row in $data )
{
    $properties = [ordered] @{};
    foreach( $mapping in $mappings )
    {
        $properties.Add($mapping.TableColumnName, $row."$($mapping.FileHeaderName)");
    }
    $values += new-object PSCustomObject -Property $properties;
}
write-host ($values | ft | out-string)

# ColumnW ColumnY ColumnZ
# ------- ------- -------
# Value P Value Q Value R
# Value S Value T Value U

棘手的一点是$properties.Add($mapping.TableColumnName, $row."$($mapping.FileHeaderName)");- 基本上,您可以使用点分字符串文字或变量访问 PowerShell 中的对象属性(我不确定确切的功能名称) - 例如

PS> $myValue = new-object PSCustomObject -Property @{ "aaa"="bbb"; "ccc"="ddd" }
PS> $myValue."aaa"
bbb

PS> $myProperty = "aaa"
PS> $myValue.$myProperty
"bbb"

计算结果为named in$row."$($mapping.FileHeaderName)"属性值的表达式也是如此。$row $mapping.FileHeaderName

最后,您可以使用现有流程将对象插入数据库...

请注意,我无法完全弄清楚您的 ArrayCompare 实际在做什么,因此上述方法可能无法 100% 解决您的问题,但希望它足够接近,您可以自己解决差异,或者在哪里发表评论它与您想要的解决方案不同。

希望这可以帮助。

于 2019-12-02T19:38:48.193 回答