Передача нескольких переменных из файла csv в invoke-sqlcmd

Я пытаюсь прочитать значения из файла CSV, встроить их в оператор INSERT T-SQL и запустить этот оператор с помощью Invoke-Sqlcmd.

Вот мой код:

Push-Location; Import-Module SQLPS -DisableNameChecking; Pop-Location

$InsertQry = "insert into $ImportTable VALUES ('`$(Col1)','`$(Col2)','`$(Col3)','`$(Col4)') "

Import-CSV $ImportFile | ForEach-Object { `
$RowData = "Col1=$($_.{My Ref})","Col2=$($_.{General satisfaction})","Col3=$($_.Helpfulness)","Col4=$($_.Effort)"
    Invoke-Sqlcmd `
    -Database $DBName -ServerInstance $SQLServer `
    -Query $InsertQry `
    -Variable $RowData
  }

Скрипт отлично работает со строками в CSV-файле, которые содержат значения для каждого столбца. К сожалению, для меня некоторые строки в файле CSV содержат пустые значения (поэтому, возможно, только первые два столбца содержат значения). Эти строки не могут быть вставлены в таблицу, и возникает следующая ошибка:

Invoke-Sqlcmd: недопустимый формат для определения новой переменной для командлета Invoke-Sqlcmd. Пожалуйста, используйте формат var = value для определения новой переменной.

Потенциально пустые столбцы - это все столбцы, которые либо пусты, либо содержат однозначные числа от 1 до 5.

Я пробовал различные способы избежать значения, привести его к другому типу данных, добавить к нему ноль или пустую строку, объединить его с нулевым значением, но я не могу найти решение, которое работает.

У меня есть контроль над таблицей назначения, поэтому я был бы счастлив передать ноль, пустую строку, нуль или любое другое значение в качестве заполнителя для пустых значений.


person paulH    schedule 25.05.2017    source источник


Ответы (2)


Согласно документации, вы должны передавать переменные в строковом массиве, где каждый элемент имеет формат «ключ = значение». Вы строите это правильно. Invoke-SQLCMD, кажется, обижается на передачу нулевых значений. Нули, конечно, происходят из пустых записей в вашем CSV. Предполагая, что вы разрешаете нули в этих столбцах, возможно, вы могли бы просто настроить запрос при каждом проходе цикла.

Push-Location; Import-Module SQLPS -DisableNameChecking; Pop-Location

$InsertQry = "insert into $ImportTable VALUES ('{0}','{1}','{2}','{3}')"
$propertiesToSplat = @{
    Database = $DBName 
    ServerInstance = $SQLServer
}

Import-CSV $ImportFile | ForEach-Object {
    $propertiesToSplat.Query = $InsertQry -f $_."My Ref", $_."General satisfaction", $_.Helpfulness, $_.Effort
    Invoke-Sqlcmd @propertiesToSplat
}

Поэтому на каждом проходе цикла мы используем оператор формата для вставки значений столбца в ваш оператор вставки. Использование фигурных скобок в именах свойств полезно, когда ваши свойства содержат специальные символы. Поскольку вам просто нужно иметь дело с пространством; цитаты работают так же хорошо.

Я также хотел показать вам сплаттинг, который является методом передачи свойств в виде хеш-таблицы в командлет. Это позволяет редактировать реквизит на лету и делать строки короче, не беспокоясь о повсюду обратных кавычках.

person Matt    schedule 25.05.2017
comment
Спасибо, Мэтт. Такой подход сработал, и сплаттинг тоже выглядит интересно. Я думаю, что это открывает возможность атак с использованием SQL-инъекций, поэтому его следует использовать с осторожностью, но в данном конкретном случае это не будет проблемой. Я не совсем уверен, что мой первоначальный подход был лучше в этом отношении? - person paulH; 26.05.2017

Изменить - совершенно новый ответ

Я отстой на ForEach-Object. Это foreach цикл, который проверяет значение «Общая оценка» для каждой строки в CSV и заменяет его строкой-заполнителем перед завершением переменной $RowData. К сожалению, я не могу проверить это здесь; пожалуйста, дайте мне знать, как у вас дела.

Push-Location; Import-Module SQLPS -DisableNameChecking; Pop-Location

$InsertQry = "insert into $ImportTable VALUES ('`$(Col1)','`$(Col2)','`$(Col3)','`$(Col4)') "

$myCSVFile = Import-CSV $ImportFile 

foreach($line in $myCSVFile){

    if($line."General staisfaction" -eq $null -or $line."General staisfaction" -eq ""){
        $line."General staisfaction" = "placeholder"
    }

    $RowData = "Col1=$($line.{My Ref})","Col2=$($line.{General satisfaction})","Col3=$($line.Helpfulness)","Col4=$($line.Effort)"

    Invoke-Sqlcmd -Database $DBName -ServerInstance $SQLServer -Query $InsertQry -Variable $RowData
}
person G42    schedule 25.05.2017
comment
Спасибо за предложение. Я изменил сценарий, чтобы поместить команду в одну строку. Это не подействовало. Помещение кавычек вокруг переменной $ RowData привело к сбою каждой строки в CSV с сообщением об ошибке, которое я упомянул в вопросе. - person paulH; 25.05.2017
comment
Однако я не думаю, что проблема в самой переменной $ RowData, она в генерации допустимых значений для переменных Col2, Col3 и Col4. Хотя я могу ошибаться в этом, так как мне не удалось исправить проблему! :-) - person paulH; 25.05.2017
comment
Спасибо. Верно, потому что он будет преобразовывать $RowData как строку - не заметил этого. Исходя из того, что проблема вызвана пустыми полями, возможно, лучший подход - решить ее в части $RowData = ... Исправит ли это замена "Col1=$($_.{My Ref})" на "Col1=""$($_.{My Ref})"""? - person G42; 25.05.2017
comment
Рассмотрите возможность использования строкового статического метода [string]::IsNullorEmpty() или [string]::IsNullorWhiteSpace() - person Matt; 25.05.2017
comment
Изменение на Col1 = $ ($ _. {My Ref}) не помогло. Я считаю, что [string] :: IsNullorEmpty () или использование цикла foreach также сработало бы, поэтому спасибо за предложения! - person paulH; 26.05.2017