Скрипт Powershell, взаимодействующий с SQL, не принимает значения с апострофами

Я пытаюсь автоматизировать вставку значений из CSV в таблицу базы данных SQL. Все отлично переносится в базу данных, за исключением имен с апострофами. Я попытался добавить второй апостроф рядом с первым в надежде, что он ускользнет, ​​но безуспешно.

Ошибка, которую я получаю, выглядит следующим образом:

Exception calling "ExecuteReader" with "0" argument(s): "Incorrect syntax near 'BRIEN'.
Incorrect syntax near 'BRIEN'.
Unclosed quotation mark after the character string '
                        WHERE NPINum = (something);
                    END
            '."
At C:\REAL SCRIPTING HOURS\NPI\NPI-SQL.ps1:232 char:13
+             $SqlDataReader = $SqlCommand.ExecuteReader();
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : SqlException

Раздел, в котором возникают проблемы, выглядит следующим образом (простите за беспорядок):

$csvData = Import-Csv -Path $moveToSQL.Item($iterator)
$correctCols = $csvData | Select-Object 'NPI', 'Provider Last Name (Legal Name)', 'Provider First Name', 'Provider Middle Name', 'Provider Credential Text'
## Get it into SQL
for ($i = 0; $i -lt $correctCols.Count; $i++) {
    ## Make sure there are no issues with apostrophe'd names (I know there's likely a more visually appealing way of doing this... but alas)
    $apostropheCheck = 0

    ## Check the last name listed
    $apostropheCheck = $correctCols.Get($i).'Provider Last Name (Legal Name)'.Contains("'")    # bool to see if an apostrophe even exists
    if ($apostropheCheck -eq 'True') {
        # if it does, do something
        $apostropheCheck = $correctCols.Get($i).'Provider Last Name (Legal Name)'.IndexOf("'") # get the index of where the apostrophe is
        $correctCols.Get($i).'Provider Last Name (Legal Name)'.Insert($apostropheCheck, "'")   # add in another one for good measure (break out)
    }                                                                                          # rinse and repeat

    ## Check the first name listed
    $apostropheCheck = $correctCols.Get($i).'Provider First Name'.Contains("'")
    if ($apostropheCheck -eq 'True') {
        $apostropheCheck = $correctCols.Get($i).'Provider First Name'.IndexOf("'")
        $correctCols.Get($i).'Provider First Name'.Insert($apostropheCheck, "'")
    }

    ## Check the middle name listed
    $apostropheCheck = $correctCols.Get($i).'Provider Middle Name'.Contains("'")
    if ($apostropheCheck -eq 'True') {
        $apostropheCheck = $correctCols.Get($i).'Provider Middle Name'.IndexOf("'")
        $correctCols.Get($i).'Provider Middle Name'.Insert($apostropheCheck, "'")
    }

    ## Create query to run
    $SqlQuery = "
    IF NOT EXISTS (SELECT 1 FROM (my.dbo.table) WHERE NPINum = {0})
        BEGIN
            INSERT INTO (my.dbo.table)
                VALUES
                ('{0}'
                ,'{1}'
                ,'{2}'
                ,'{3}'
                ,'{4}')
        END
    ELSE
        IF NOT EXISTS
                (SELECT 1 FROM (my.dbo.table)
                WHERE NPINum = {0} AND LastName = '{1}'
                AND FirstName = '{2}'
                AND MiddleName = '{3}'
                AND Credentials = '{4}')
            BEGIN
                UPDATE (my.dbo.table)
                SET LastName = '{1}', FirstName = '{2}', MiddleName = '{3}', Credentials = '{4}'
                WHERE NPINum = {0};
            END
    " -f $correctCols.Get($i).NPI, $correctCols.Get($i).'Provider Last Name (Legal Name)', $correctCols.Get($i).'Provider First Name', $correctCols.Get($i).'Provider Middle Name', $correctCols.Get($i).'Provider Credential Text'
    $SqlCommand.CommandText = $SqlQuery
    ## Open up SQL communication and execute query in the database
    $SqlConnection.Open()
    $SqlDataReader = $SqlCommand.ExecuteReader()
    $SqlConnection.Close()
}

Ужасная идея использовать String.Format (-f) вместо .Parameters.AddWithValue(...) в вашем запросе.

Santiago Squarzon 15.04.2024 18:53

Прошу прощения, я новичок в PowerShell и относительно новичок в SQL. Не могли бы вы объяснить, почему? Кроме того, поможет ли это решить проблему, с которой я столкнулся?

Avarsiral 15.04.2024 19:03

избегает SQL-инъекций и уже обрабатывает экранирование ' за вас

Santiago Squarzon 15.04.2024 19:04
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
3
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Рекомендуется избегать String.Format (-f) любой ценой. Ручная интерполяция значений в строке запроса открывается для атак SQL-инъекций.

Вместо этого вам следует использовать метод .AddWithValue из класса SqlParameter, таким образом API будет обрабатывать интерполяцию значений за вас, избегая этой уязвимости и обрабатывая правильное экранирование знаков препинания (т.е.: обработку проблема, с которой вы столкнулись, избегая одинарных кавычек ').

Вот как более или менее должен выглядеть ваш код. Я значительно уменьшаю количество параметров, которые нужно ввести здесь, чтобы дать упрощенный ответ. Ответ также предполагает, что в вашем CSV есть столбцы Foo, Bar, Baz.

$command = [System.Data.SqlClient.SqlCommand]::new()
$command.Connection = $connection # <= SQLConnection object needs to be defined beforehand
$command.CommandText = @'
IF NOT EXISTS (SELECT 1 FROM (my.dbo.table) WHERE NPINum = @foo)
    BEGIN
        INSERT INTO (my.dbo.table)
            VALUES (@foo, @bar, @baz)
    END
ELSE
    IF NOT EXISTS
        (SELECT 1 FROM (my.dbo.table)
            WHERE NPINum = @foo AND LastName = @bar
            AND FirstName = @baz)
        BEGIN
            UPDATE (my.dbo.table)
            SET LastName = @bar, FirstName = @baz
            WHERE NPINum = @foo;
        END
'@

try {
    $command.Connection.Open()

    foreach ($line in $csv) {
        try {
            $command.Parameters.Clear()
            $null = $command.Parameters.AddWithValue('@foo', $line.Foo)
            $null = $command.Parameters.AddWithValue('@bar', $line.Bar)
            $null = $command.Parameters.AddWithValue('@baz', $line.Baz)
            $reader = $command.ExecuteReader()
        }
        catch {
            # handle possible writing errors here
        }
    }
}
finally {
    if ($command.Connection) {
        $command.Connection.Dispose()
        $command.Dispose()
    }
}

Другие вопросы по теме