#sql-server #powershell
#sql-сервер #powershell
Вопрос:
У меня есть простой SQL-файл, который создает новую базу данных на основе 3 отправленных входных параметров: dbName, datafileName и LogFileName
Но, когда я вызываю этот скрипт через powershell, вызывая командлет Invoke-Sqlcmd, я получаю следующую ошибку:
Неправильный синтаксис возле 'CommonDB'. Неправильный синтаксис рядом с ''. Неправильный синтаксис возле 'CommonDB'. Неправильный синтаксис рядом с 'E:'. Метка 'E' уже объявлена. Имена меток должны быть уникальными в пакете запросов или хранимой процедуре. Неправильный синтаксис рядом с 'E:'. Метка 'E' уже объявлена. Имена меток должны быть уникальными в пакете запросов или хранимой процедуре. Неправильный синтаксис возле 'CommonDB'. Метка 'E' уже объявлена. Имена меток должны быть уникальными в пакете запросов или хранимой процедуре. Метка 'E' уже объявлена. Имена меток должны быть уникальными в пакете запросов или хранимой процедуре. В строке: 1 символ: 1 Invoke-Sqlcmd -ServerInstance localhostsql2012 -Мастер базы данных -Использование... ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : недопустимая операция: (:) [Invoke-Sqlcmd], исключение SqlPowerShellSqlExecutionException FullyQualifiedErrorId: ошибка SqlError, Microsoft.SQLServer.Management.PowerShell.GetScriptCommand
Моя версия Powershell:
Основная Незначительная ревизия сборки ----- ----- ----- -------- 5 1 16299 1004
Это мой файл CreateDatabase.sql:
USE MASTER
GO
SET QUOTED_IDENTIFIER ON;
GO
DECLARE
@DbName nvarchar(50),
@sql nvarchar(max),
@datafileName nvarchar(500),
@logfileName nvarchar(500)
SET @sql = N''
SELECT @DbName = N'$(dbName)'
SELECT @datafileName = N'$(datafileName)'
SELECT @logfileName = N'$(logfileName)'
IF NOT EXISTS (SELECT * FROM master.sys.databases WHERE name = @DbName)
BEGIN
SELECT @sql =
N'CREATE DATABASE "' @DbName N'"'
N' ON PRIMARY ( NAME = [' @DbName '] ,'
N' FILENAME = [' @datafileName '],'
N' SIZE = 1GB , MAXSIZE = UNLIMITED, FILEGROWTH = 512MB )'
N' LOG ON ( NAME = [' @DbName '_log] ,'
N' FILENAME = [' @logfileName '], SIZE = 100MB , FILEGROWTH = 10% )'
END
IF (@sql <> N'')
EXEC (@sql)
GO
SET QUOTED_IDENTIFIER OFF;
GO
Это содержимое моего файла PS1:
$params=@("dbName='CommonDB'", "datafileName='E:my filessql.2012dataCommonDb.mdf'", "logfileName='E:my filessql.2012dataCommonDb_log.ldf'")
Invoke-Sqlcmd -ServerInstance localhostsql2012 -Database master -Username sa -Password xxxx -ErrorAction Stop -InputFile .CreateDatabase.sql -Variable $params
Но, если я запускаю это из командной строки PS, все работает просто отлично:
PS E:My Scripts> Invoke-Sqlcmd -ServerInstance localhostsql2012 -Database master -Username sa -Password xxxx -ErrorAction Stop -InputFile .CreateDatabase.sql -Variable dbName='CommonDB', datafileName='E:my filessql.2012dataCommonDb.mdf', logfileName='E:my filessql.2012dataCommonDb_log.ldf'
Ответ №1:
Попробуйте :
$params=@{"dbName"="CommonDB"; "datafileName"="E:my filessql.2012dataCommonDb.mdf"; "logfileName"="E:my filessql.2012dataCommonDb_log.ldf"}
или
$dbName = "CommonDB"
$datafileName = "E:my filessql.2012dataCommonDb.mdf"
$logfileName = "E:my filessql.2012dataCommonDb_log.ldf"
Invoke-Sqlcmd -ServerInstance localhostsql2012 -Database master -Username sa -Password xxxx -ErrorAction Stop -InputFile .CreateDatabase.sql -Variable dbName=$dbName, datafileName=$datafileName, logfileName=$logfileName
Комментарии:
1. спасибо за ответ. Вариант 1 не будет работать, поскольку
@params
это массив строк, и вы не можете иметь пары имя-значение в массиве строк. Вариант 2 работает в этом конкретном случае. Но я хочу, чтобы мой скрипт был универсальным. Он должен иметь возможность вызывать другие файлы .sql, имена и значения параметров которых динамически изменяются.
Ответ №2:
Хорошо, я заставил эту штуку работать в общем виде, сгенерировав скрипт, необходимый для вызова-sqlcmd. Написал функцию, которая принимает файл сценария и его параметры и генерирует ScriptBlock из того же:
<#
.SYNOPSIS
#
.DESCRIPTION
This function invokes sql in a .sql file and passes the parameters to it as needed.
.PARAMETER script_file
$script_file - The relative or absolute path to the sql file that needs to be executed
$parameter_names - a list of string containing the names of the parameters which need to be passed to the sql file
.EXAMPLE
For example if there is a SQL file which takes 3 parameters: dbName, datafileName, and logfileName, whose values are:
"CommonDB", "E:my filessql.2012dataCommonDb.mdf", and "E:my filessql.2012dataCommonDb_log.ldf" repectively,
this function first generates the following ScriptBlock for the same and then executes it by calling the Invoke-Command cmdlet:
$dbName = "CommonDB"
$datafileName = "E:my filessql.2012dataCommonDb.mdf"
$logfileName = "E:my filessql.2012dataCommonDb_log.ldf"
Invoke-Sqlcmd -ServerInstance localhostsql2012 -Database master -Username sa -Password xxxx -ErrorAction Stop -InputFile .CreateDatabase.sql -Variable dbName=$dbName, datafileName=$datafileName, logfileName=$logfileName
#>
Function Invoke-SQLFile( [string] $script_file, [System.Collections.Generic.List[string]]$parameter_names) {
#Assuming that $serverInstance, $db, $username, and $password are global variables
$invokeSql_Script = "Invoke-Sqlcmd -ServerInstance `"$serverInstance`" -Database `"$db`" -Username `"$username`" -Password `"$password`" -ErrorAction Stop -InputFile `"$script_file`" "
$scriptStr = ""
#The script file may or may not accept parameters
if(($null -ne $parameter_names) -and ($parameter_names.Count -gt 0)){
$invokeSqlVariableStr = ""
foreach($param in $parameter_names){
#There would be a global variable with the same name as the parameter_name
# e.g. If the param name is "databaseName", there should be a global variable called $databaseName
$paramVal = Get-Variable -Name $param | Select-Object -Property Value
$scriptStr = "$scriptStr `$$param = `"$($paramVal.Value)`";`r`n"
$invokeSqlVariableStr = "$invokeSqlVariableStr $param=`"`$$param`", "
}
#remove the trailing ".," from the string
$invokeSqlVariableStr = $invokeSqlVariableStr -replace ".{2}$"
$scriptStr = "$scriptStr $invokeSql_Script -Variable $invokeSqlVariableStr"
}
else {
#if no parameters need to be passed, then the $invokeSql_Script is good enough
$scriptStr = $invokeSql_Script
}
$scriptBlock = [System.Management.Automation.ScriptBlock]::Create($scriptStr)
Invoke-Command -ScriptBlock $scriptBlock
}
Спасибо @rAJ за эту идею.
Тем не менее, я все еще не знаю, почему мой первоначальный скрипт не удался. И указатели там приветствуются.