Преобразование типов данных при использовании Import-CSV - PullRequest
1 голос
/ 02 мая 2020

Мне нужно построить процесс, который включает чтение файла CSV в таблицу SQL Server. В прошлом я использовал SQL Bulk Insert, но файлы CSV, с которыми я работаю, будут иметь запятые в некоторых полях (непредсказуемо), и моя версия sql server (2016) не поддерживает их передачу (я так понимаю, с 2017 года параметр FIELDQUOTE решает эту проблему).

В этом посте говорится об этой самой проблеме, и я включил этот метод использования Powershell с Import-CSV . Мой набор данных при каждом запуске будет очень маленьким, поэтому «Простой импорт в CSV с помощью PowerShell» должен подойти. Однако я зацикливаюсь на типах данных.

Когда я использую этот метод ниже, я получаю ошибки, потому что он не может преобразовать строку в десятичное или время данных. Однако, если я настрою свою таблицу sql так, чтобы она содержала только поля строкового типа, данные импортируются правильно. Кажется, игнорировать строку заголовка. Я понимаю, что Import-CSV будет использовать первую строку в качестве заголовков по умолчанию, но то, что я получаю в моей целевой таблице (когда все поля установлены на varchar (n)), это только строки 2 и более.

Есть ли способ для меня использовать этот метод, но установить поля в их надлежащие форматы, чтобы они могли go в правильно определенную таблицу sql? Если бы мне пришлось, я мог бы работать с ним как У меня это работает (все столбцы назначения установлены на varchar (n)), но я бы предпочел, чтобы столбцы sql были правильно определены. Мои имена заголовков CSV предсказуемы, и я знаю, каким должен быть каждый тип данных столбца. Многие из них останутся в виде строки, но некоторые должны быть заменены на десятичные (19,4) или datetime. Я думаю, что я читаю в других местах, где я могу передать команду where-object или что-то еще, но я новичок PS и изо всех сил пытаюсь найти правильный синтаксис.

$ConnectionString = "Data Source=<myServer>; Database=<myDatabase>;Trusted_Connection=True;";
. "E:\Out-DataTable.ps1"

$csvDataTable = Import-CSV -Path "<sourceCsvFile>" | Out-DataTable
$bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $ConnectionString
$bulkCopy.DestinationTableName = "<myDestinationTable>"
$bulkCopy.WriteToServer($csvDataTable)

1 Ответ

0 голосов
/ 02 мая 2020

Относительно вашего основного вопроса: «Есть ли способ для меня использовать этот метод, но установить для полей их надлежащие форматы, чтобы они могли go в правильно определенной таблице sql?» Да .

Вы могли бы привести значения к нужным им типам перед созданием DataTable (пример кода ниже). Лично я, вероятно, попытался бы заменить файлы CSV чем-то, что кодирует информацию о типе, например JSON или паркет.


ОБНОВЛЕНИЕ НА ОСНОВЕ ВОПРОСОВ ОП:

Должен ли я прописать каждое поле в этом l oop, или только те, которые нужно изменить со строки на что-то еще? Вам нужно сопоставить каждое поле с [PSCustomObject], но вы только Нужно привести поля, содержащие данные, которые должны быть не строковыми.

Для заголовков столбцов, в которых есть пробелы, заключать их в квадратные скобки при обращении к ним в PS (например, [Дата создания]) Я скорректировал ответ, чтобы проиллюстрировать один из возможных способов обрабатывать заголовки столбцов с пробелами. См. Столбец 'e e'.

Я вижу, что ваш пример преобразует числа типа 0,2 в "double". Мои десятичные данные будут в валюте и будут работать с другими данными, так что я установлю их в десятичном виде. Ожидаете ли вы, что это будет проходить правильно, или существует другой правильный синтаксис для преобразования в десятичное число? Исходя из приведенной ниже ссылки, десятичный тип. NET должен отображаться на SQL десятичный тип сервера. Смотрите свойство f в данных примера. https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-data-type-mappings



При условии, что у вас есть следующие данные в CSV

a,b,c,d,e e,f
string1,1,0.1,2020-05-02 14:29:06.780,e,1.99
string2,2,0.2,2020-05-03 01:01:02.000,e,3.59


Вы можете привести значение к желаемому типы, использующие подход, аналогичный описанному в foreach l oop ниже. Очевидно, вам придется настроить его под свои фактические данные.

param(
    $datasource = 'localhost',
    $db = 'test',
    $destTable = 'table',
    $csvPath = "$PSScriptRoot\data.csv"
)

$ConnectionString = "Data Source=$datasource; Database=$db;Trusted_Connection=True;";
. "$PSScriptRoot\Out-DataTable.ps1"

$rawData = Import-CSV -Path $csvPath
$typedData = @()
foreach ($row in $rawData) {
    $tempObj = [PSCustomObject] @{
        a = $row.a
        b = [int] $row.b
        c = [double] $row.c
        d = [datetime] $row.d
        e_e = $row."e e"
        f = [decimal] $row.f
    }
    $typedData += $tempObj
}

$csvDataTable = $typedData | Out-DataTable
$bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $ConnectionString
$bulkCopy.DestinationTableName = $destTable
$bulkCopy.WriteToServer($csvDataTable)

Содержимое Out-DataTable.ps1 (для будущих читателей)

####################### 
function Get-Type { 
    param($type) 

    $types = @( 
        'System.Boolean', 
        'System.Byte[]', 
        'System.Byte', 
        'System.Char', 
        'System.Datetime', 
        'System.Decimal', 
        'System.Double', 
        'System.Guid', 
        'System.Int16', 
        'System.Int32', 
        'System.Int64', 
        'System.Single', 
        'System.UInt16', 
        'System.UInt32', 
        'System.UInt64') 

    if ( $types -contains $type ) { 
        Write-Output "$type" 
    } 
    else { 
        Write-Output 'System.String' 

    } 
} #Get-Type 

####################### 
<# 
.SYNOPSIS 
Creates a DataTable for an object 
.DESCRIPTION 
Creates a DataTable based on an objects properties. 
.INPUTS 
Object 
    Any object can be piped to Out-DataTable 
.OUTPUTS 
   System.Data.DataTable 
.EXAMPLE 
$dt = Get-psdrive| Out-DataTable 
This example creates a DataTable from the properties of Get-psdrive and assigns output to $dt variable 
.NOTES 
Adapted from script by Marc van Orsouw see link 
Version History 
v1.0  - Chad Miller - Initial Release 
v1.1  - Chad Miller - Fixed Issue with Properties 
v1.2  - Chad Miller - Added setting column datatype by property as suggested by emp0 
v1.3  - Chad Miller - Corrected issue with setting datatype on empty properties 
v1.4  - Chad Miller - Corrected issue with DBNull 
v1.5  - Chad Miller - Updated example 
v1.6  - Chad Miller - Added column datatype logic with default to string 
v1.7 - Chad Miller - Fixed issue with IsArray 
.LINK 
http://thepowershellguy.com/blogs/posh/archive/2007/01/21/powershell-gui-scripblock-monitor-script.aspx 
#> 
function Out-DataTable { 
    [CmdletBinding()] 
    param([Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [PSObject[]]$InputObject) 

    Begin { 
        $dt = new-object Data.datatable   
        $First = $true  
    } 
    Process { 
        foreach ($object in $InputObject) { 
            $DR = $DT.NewRow()   
            foreach ($property in $object.PsObject.get_properties()) {   
                if ($first) {   
                    $Col = new-object Data.DataColumn   
                    $Col.ColumnName = $property.Name.ToString()   
                    if ($property.value) { 
                        if ($property.value -isnot [System.DBNull]) { 
                            $Col.DataType = [System.Type]::GetType("$(Get-Type $property.TypeNameOfValue)") 
                        } 
                    } 
                    $DT.Columns.Add($Col) 
                }   
                if ($property.Gettype().IsArray) { 
                    $DR.Item($property.Name) = $property.value | ConvertTo-XML -AS String -NoTypeInformation -Depth 1 
                }   
                else { 
                    $DR.Item($property.Name) = $property.value 
                } 
            }   
            $DT.Rows.Add($DR)   
            $First = $false 
        } 
    }  

    End { 
        Write-Output @(, ($dt)) 
    } 

} #Out-DataTable
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...