2016-07-14 44 views
0

我是一名軟件人員,但在PowerShell知識的第二週。PowerShell - 將大固定寬度文件與大型SQL表進行比較

我們有一組12個固定寬度的格式文件,其中包含人員列表(記錄可能被複制)。這些文件大約800MB,總行數約爲1400萬。看第一個文件,它包含1,201,940行。

此外,我們有一個SQL表,應該包含所有的數據(不同的記錄)。我的任務是使用PowerShell通過將源文件中的幾個選擇字段與SQL表進行比較,然後將任何缺失的記錄寫入CSV日誌來確保數據完全加載。

我們假設我感興趣的字段是ID,FirstName,LastName,並且對於所有情況,我限制我的對象/查詢只考慮這些字段。

在PowerShell中比較數據最理想的方法是什麼?您是否將數據綁定到SQL,使其完成工作,然後檢索結果,或將所有數據捆綁到PowerShell並在其中進行工作?

我想過以下的思路,但沒有測試過:

  1. 創建一個SQL表變量(@fileInfo)。從文件($dtFile)創建一個DataTable。使用$dtFile,對於每X行,加載@fileInfo。在@fileInfo和SQL表之間執行LEFT JOIN,並將結果推送到DataTable($dtResults)中。將$dtResults寫入日誌。清空@fileInfo的內容以準備循環的下一次迭代。這似乎是我最好的想法。
  2. 從文件($dtFile)創建一個DataTable。使用$dtFile,對於每X行,構造一個SQL查詢語句,該語句有一個可怕的WHERE子句,該子句限制數據庫返回的行。在另一個DataTable中移動($dtSQL)。比較兩者並記錄$dtFile中未出現在$dtSQL中的任何條目。看起來很嚴重,但有效。
  3. 將文件中的所有1.2m記錄載入DataTable。將它們批量插入SQL臨時表,LEFT JOIN針對SQL表,檢索結果並將結果寫入日誌。我假設我會因爲在網絡上推送一堆數據而陷入困境
  4. 將SQL表中的所有記錄加載到DataTable,將文件中的所有記錄加載到第二個DataTable中,比較PowerShell中的結果並將結果寫入日誌。我想我會用完內存......?

我會爲每個解決方案創建腳本並自己做一個測試,但是我處於時間緊縮之中,沒有奢華。情況並不總是這樣嗎?

編輯:我張貼工作對我來說低於

+1

您是否可以訪問同一服務器或其他可訪問的服務器上的其他數據庫? 1.2M行並不是那麼大。您可能只需執行整個csv文件的標準bcp批量插入。在一臺體面的服務器上最多3小時。請按照批量插入指南https://technet.microsoft.com/en-us/library/ms177445%28v=sql.105%29.aspx?f=255&MSPPError=-2147217396即表鎖!和索引管理!和日誌管理!!!!!。 – Gareth

+0

http://stackoverflow.com/questions/2479434/run-a-shell-command-with-arguments-from-powershell-script – Gareth

+0

你能顯示一個固定寬度輸入文件的樣本/片段嗎? –

回答

0

對不起所有沒有被及時回覆上,但我有一個解決方案!最有可能改進的地方,但解決方案相當快。我沒有訪問我的數據庫來直接運行PowerShell,所以我最後使用了SQL導入和導出嚮導。

摘要過程:

  1. 創建的數據點,這將被消耗在PowerShell中的對象陣列的CSV文件。
  2. 找到感興趣的文件,並將其存儲到字符串數組(可能是沒有必要的,但它似乎更快)通過你的字符串數組中找到的每個文件
  3. 週期。對於每個文件,打開一個.NET StreamReader,併爲每一行分析文件與數據點的對象數組,以創建您寫入單個合併的分隔輸出文件的子字符串。我推薦管道字符(看起來像這樣:|,通常在之上,輸入),因爲它通常在數據中找不到,而流氓逗號或製表符可能會讓你感覺不舒服。
  4. 一旦劇本完成後,使用SQL導入嚮導輸出文件中創建一個表

詳細

  1. 創建列出了A列數據點的CSV文件,列B中的起始位置和列C中的寬度。它看起來像這樣: data point CSV file
  2. 通過利用對象在腳本中導入數據點陣列。

    $dataPoints = Import-Csv "c:\temp\datapoints.csv" 
    $objDataCols = @() 
    foreach($objCol in $dataPoints){ 
        objColumn = New-Object psobject 
        $objColumn | Add-Member -Type NoteProperty -Name Name -Value $objCol.Name 
        $objColumn | Add-Member -Type NoteProperty -Name Position -Value ([int] $objCol.Position) 
        $objColumn | Add-Member -Type NoteProperty -Name ColumnLength -Value ([int] $objCol.ColumnLength) 
        $objSourceCols += $objColumn 
    } 
    
  3. 查找文件並將名稱組裝到一個數組(可選)。我用一個正則表達式來篩選我的文件。

    $files = @() 
    Get-ChildItem -Path $sourceFilePath | Where-Object { $_.FullName -match $regExpression } | ForEach-Object{ 
        $files += $_.FullName 
    } 
    
  4. 遍歷每個文件並將它們解析爲輸出文件。在生產代碼中,你會想嘗試/ catch塊,但是我在示例中將它們留在了外面。

    $writer = New-Object System.IO.StreamWriter "c:\temp\outputFile.txt" 
    ForEach($sourceFileName in $files){ 
        $reader = [System.IO.File]::OpenText($sourceFileName) 
        while($reader.Peek() -gt -1){ 
         $line = $reader.ReadLine() 
    
         # Write each data point in the line, pipe delimited 
         for($i = 0; $j -le ($objDataCols).Length; $i++){ 
          # Write to a pipe-delimited file 
          $writer.Write("{0}|", $line.Substring($objDataCols[$i].Position, $objDataCols[$i].ColumnLength)) 
         } 
    
         # Write a new line, along with any additional reference columns not defined in the source file, such as adding in the source file name and line number 
         $writer.WriteLine($sourceFileName) 
        }  
        $reader.Close()   
        $reader.Dispose() 
    } 
    $writer.Close() 
    $writer.Dispose() 
    
  5. 將管道分隔的輸出文件導入到SQL中。

1

我將完全卸載數據庫引擎進行比較的解決方案:使用類似Import-CsvToSql

  1. 批量加載數據到SQL(或bcp )到新表fileTable
  2. 比較fileTableoriginalTableusing UNION ALL(見下文)
  3. 日誌結果(即。差異)到一個文件。

取決於底層存儲,您可能需要原始表複製到一個數據庫,在那裏,你可以從文件中


UNION ALL導入數據集之前恢復模式切換到SIMPLE或BULK_LOGGED基於比較程序會看起來像:

SELECT MIN(TableName) as TableName, ID, FirstName, LastName 
FROM 
(
    SELECT 'Database' as TableName, originalTable.ID, originalTable.FirstName, originalTable.LastName 
    FROM originalTable 
    UNION ALL 
    SELECT 'Files' as TableName, fileTable.ID, fileTable.FirstName, fileTable.LastName 
    FROM fileTable 
) tmp 
GROUP BY ID, FirstName, LastName 
HAVING COUNT(*) = 1 
ORDER BY ID 
+0

您也可以使用'EXCEPT'集合運算符來查找任何缺少的結果 – mheptinstall