I have a stored procedure in a PowerShell (version 4) script that logs the results of a job
function Log-Build($lastExitCode, $result, $taskId) {
$date = Get-Date
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
$cmd = New-Object System.Data.SqlClient.SqlCommand
$myResult = $result -join "<br/>`r`n" |Out-String
$cmd.Connection = $connection;
$cmd.CommandText = "LSBuild_LogAndMailResult";
$cmd.Parameters.AddWithValue("#Task_ID",[string]$taskId);
$cmd.Parameters.AddWithValue("#Result",[string]$myResult);
$cmd.Parameters.AddWithValue("#Deployment_Status",[int]$lastExitCode);
$connection.Open()
$cmd.ExecuteNonQuery() | Out-Null
$connection.Close()
}
However, when I run it, I always get the following error response:
Exception calling "ExecuteNonQuery" with "0" argument(s): "Procedure or function 'LSBuild_LogAndMailResult' expects parameter '#Deployment_Status',
which was not supplied.
I checked in the terminal window in Powershell ISE to make sure the parameter was there ( $cmd.Parameters["#Deployment_Status"].value;) and it is definitely being added. Not sure why it's not getting passed to the database
You must set command type as stored procedure
$cmd.CommandType = [System.Data.CommandType]::StoredProcedure
Related
is there any way to capture specific error message while this call to store that error message in sql table ?
function Get-SqlData {
param([string]$serverName=$(throw 'serverName is required.'), [string]$databaseName=$(throw 'databaseName is required.'),
[string]$query=$(throw 'query is required.'))
try {
Write-Verbose "Get-SqlData serverName:$serverName databaseName:$databaseName query:$query"
$connection = new-object system.data.sqlclient.sqlconnection( "Data Source=$serverName;Initial Catalog=$databaseName;Integrated Security=SSPI;")
$adapter = new-object system.data.sqlclient.sqldataadapter ($query, $connection)
$table = new-object system.data.datatable
[void]$adapter.Fill($table) #| out-null
$table
} catch {
write-host $Server
write-host 'Connection issue'
}
}
$Query = "set nocount on; SELECT CASE WHEN Is_Clustered = 1 THEN SQLClusterName ELSE ServerName END FROM Server_Master_List WHERE Is_Monitored = 1 "
$Servers = sqlcmd -b -S XYZ-XYZ -d DBA -h -1 -Q $Query -W
$sqltbl = #()
foreach($Server in $Servers) { $sqltbl += Get-SqlData $Server 'master' $qry }
#$sqltbl
<#Insert data from Powershell variable to SQL table #>
$connectionString = "Server=$env:ComputerName;Database=DBA;Integrated Security=SSPI;"
Yes. You can tell PowerShell to only catch certain types of exceptions.
For example...
$serverName = 'SOMERANDOMSERVER'
$databaseName = 'DoesntMatter'
$query = 'SELECT 1'
try {
$connection = New-Object System.Data.SqlClient.SqlConnection ("Data Source=$serverName;Initial Catalog=$databaseName;Integrated Security=SSPI;")
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter ($query, $connection)
$table = New-Object System.Data.DataTable
[void]$adapter.Fill($table)
$table
} catch [System.Data.SqlClient.SqlException] {
'CAUGHT A SQL EXCEPTION!!'
} catch {
'Caught some other type of exception'
}
However, if you want to get further into the details, you'll need to start parsing the exceptions themselves.
And that's where this leads me to ask...why do you need to do this? A query with bad syntax, a query that throws an error, an unavailable server...those will all return a SqlException. Do you plan on implementing something which handles each of these exceptions in a particular way?
Personal opinion:
Any time I see someone starting to write code in PowerShell for running SQL queries, my first question is...Are you trying to build some sort of maintenance/utility script where it's okay to utilize existing community modules? If so, you need to look up dbatools. It's a PowerShell module that is packed with cmdlets that handle all this stuff for you. For example, you've basically just written their cmdlet called Invoke-DbaQuery
Another tip...learn about advanced parameters in PowerShell. You can add various checks against parameters to ensure they are mandatory, and even include verification checks to ensure the parameter values are valid prior to executing the script. That would allow you to properly implement required parameters, and you can remove the hack you've used here.
I need to check for general errors with my sql, like make sure it connects ok, make sure it returns with the query results ok, etc. I tried try/catch, but even though I know my login is wrong, it's not finding an error with the catch. I had found the try/catch idea at try catch.
How do I identify or catch when it doesn't logon to the db, or returns 0 results?
This is what I have so far:
function SQLQueryWriteToFile([string]$SQLquery, [string]$extractFile, [string]$facility)
{
#create sql connection to connect to the sql DB
try{
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection
$sqlConnection.ConnectionString = "Server=blah;Database=blah;User ID=blah;Password=blah" #sql connection
$sqlConnection.Open()
#Create a SqlCommand object to define the query
$sqlCmd = New-Object System.Data.SqlClient.SqlCommand
$sqlCmd.CommandText = $SQLquery
$sqlCmd.Parameters.Add('#facility',$facility)
$sqlCmd.Connection = $sqlConnection
#create a SqlAdapter that actually does the work and manages everything
$sqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$sqlAdapter.SelectCommand = $sqlCmd
$sqlAdapter.SelectCommand.CommandTimeout=300 #set timeout for query execution to 5 minutes (60x5=300)
#create an empty dataSet for the query to fill with its results
$dataSet = New-Object System.Data.DataSet
#execute the query and fill the dataSet (then disconnect)
$sqlAdapter.Fill($dataSet)
$sqlConnection.Close()
#dump the data to csv
$DataSet.Tables[0] | Export-Csv $extractFile #this may not be comma delimited...need to check
} #try
catch{
if($LASTEXITCODE -ne 0) #if there was an error, record it
{
Write-host $PSItem.ToString()
$global:ErrorStrings.Add("Exception: $LASTEXITCODE ; $PSItem.ToString() ")
}
else
{
$global:ErrorStrings.Add("Exception: $LASTEXITCODE ; $PSItem.ToString() ")
}
} #catch
}
I wrote a script whose purpose is to take all the files under a particular folder and in a loop to perform on the files SQL procedures in parallel (3 files at the same time).
I've seen some timeouts in the database but Powershell does not give me an error message about that. Is there a way to get errors about these timeouts?
I tried to deal with the problem by increasing the timeout value and yet the problem is not solved so I'll have to at least have a test on the subject or an error message.
I added the increased timeout to the script in the connection string and with: $Command.CommandTimeout =
param($fileName)
Import-Module sqlps -DisableNameChecking
$sqlserver = "SQLSERVERNAME"
$Connection = New-Object System.Data.SQLClient.SQLConnection
$Connection.ConnectionString = "server=SQLSERVERNAME;integrated security = True;connection Timeout=60;"
$Connection.Open()
$Command = $Connection.CreateCommand()
$Command.CommandTimeout = 60
#Start procedure
$Command.CommandText = "EXEC [DBNAME].[dbo].[usp_procname] #FileName"
$Command.Parameters.AddWithValue("#FileName", $fileName)
$Command.Connection = $Connection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $Command
$DataTable = New-Object System.Data.DataTable
$SqlAdapter.Fill($DataTable)
$Connection.Close()
I would really appreciate your help.
Thisis part of a bigger script which finds DatabaseFiles on a SQL Server machine (multiple instances).
Following should just return all files for 3 instances.
The server is called V3000801 and there is one default instance + 2 named instances on there. It's ok with me if either default or named doesn't work I'll work around this alone (most likely create a flag and do default with another connection string).
$SqlCmd.ExecuteNonQuery() just returns -1 which does not make any sense for me.
Thanks for the help
for($i=0;$i -lt $instances.Length;$i++){
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection;
$Server= "V3000801\"+$instances[$i];
$SqlConnection.ConnectionString = "Server = $server ; Database = master; Integrated Security = sspi;trusted_connection=true";
$sqlQuery="SELECT physical_name FROM sys.master_files;";
Write-Host $SqlConnection.ConnectionString;
$SqlConnection.Open();
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand;
$SqlCmd.CommandText = $sqlQuery;
$SqlCmd.Connection = $SqlConnection;
$SqlCmd.ExecuteNonQuery();
$SqlConnection.Close();
}
the link posted by #jody contains some good information
try:
$dr= $SqlCmd.ExecuteReader()
while ($dr.Read())
{
$dr.GetValue(0)
}
$sqlconnection.Close()
Use the ExecuteReader function for selects. ExecuteNonQuery is used for operations that do not return any results such as inserts, updates and deletes.
Here is an example in .NET but it should be similar in PowerShell.
EDIT:
This code should work. I tried it out on my own environment (with a different server name).
for($i=0;$i -lt $instances.Length;$i++){
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection;
$Server= "V3000801\"+$instances[$i];
$SqlConnection.ConnectionString = "Server = $server ; Database = master; Integrated Security = sspi;trusted_connection=true";
$sqlQuery="SELECT physical_name FROM sys.master_files;";
Write-Host $SqlConnection.ConnectionString;
$SqlConnection.Open();
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand;
$SqlCmd.CommandText = $sqlQuery;
$SqlCmd.Connection = $SqlConnection;
$reader = $SqlCmd.ExecuteReader();
while ($reader.Read())
{
"pfad=" $reader["physical_name"];
};
$SqlConnection.Close();
}
The error received is "The SqlParameterCollection only accepts non-null SqlParameter type objects, not SqlCommand objects." & "Procedure or function 'usp__SingleUpdateServerBackupPath' expects parameter '#decServerName', which
was not supplied."
PowerShell code:
Set-StrictMode -Version 1.0
function update-serverbackuppath {
param(
[Parameter(Mandatory=$True,ValueFromPipeLine=$True)][object[]]$inputobject
)
BEGIN {
$connection = New-Object -TypeName System.Data.SqlClient.SqlConnection
$connection.ConnectionString = "server=servername;database=database;trusted_connection=yes"
$connection.Open()
}
PROCESS {
$UpdateBackupPath = New-Object -TypeName System.Data.SqlClient.SqlCommand
$UpdateBackupPath.Connection = $connection
$UpdateBackupPath.CommandText = "usp__SingleUpdateServerBackupPath"
$UpdateBackupPath.Commandtype = [System.Data.Commandtype]::StoredProcedure
$ParamUpdateBackupPath = New-Object -TypeName System.Data.SqlClient.SqlParameter
$ParamUpdateBackupPath.ParameterName = "#decBackupPath"
$ParamUpdateBackupPath.SqlDbType = [System.Data.SqlDbType]::VarChar
$ParamUpdateBackupPath.Direction = [System.Data.ParameterDirection]::Input
$ParamUpdateBackupPath.Value = $inputobject.paths
$ParamUpdateBackupPathServerName = New-Object -TypeName System.Data.SqlClient.SqlCommand
$ParamUpdateBackupPathServerName.ParameterName = "#decServerName"
$ParamUpdateBackupPathServerName.SqlDbType = [System.Data.SqlDbType]::VarChar
$ParamUpdateBackupPathServerName.Direction = [System.Data.ParameterDirection]::Input
$ParamUpdateBackupPathServerName.Value = $inputobject.names
$UpdateBackupPath.Parameters.Add($ParamUpdateBackupPath)
$UpdateBackupPath.Parameters.Add($ParamUpdateBackupPathServerName)
$reader = New-Object -TypeName System.Data.SqlClient.SqlDataReader = $UpdateBackupPath.ExecuteReader()
}
END {
$connection.Close()
}
}
SQL Procedure:
Create Procedure usp__SingleUpdateServerBackupPath
(
#decBackupPath AS varchar(50),
#decServerName AS varchar(50)
)
AS
UPDATE BCKP
SET PTH = #decBackupPath
FROM BCKP
INNER JOIN SRVR
ON SRVR.ID = BCKP.FK_SRVR
WHERE SRVR.NM = #decServerName
CSV File Format
Import-Csv -Path C:\Bin\Repos\Backup.csv | C:\Bin\Scripts\update-serverbackuppath.ps1
Names Paths
Server1 \\fileshare\server_name
The Powershell code has several syntax errors, like referring to enums in erroneus a way:
# Incorrect form
[System.Data.Commandtype.StoredProcedure]
# Correct form for referring to enumeration
[System.Data.Commandtype]::StoredProcedure
Later on, there is an undeclared object which's member method is called:
# $command is not set, so ExecuteReader method is available
$reader = $command.ExecuteReader()
It is highly recommended to use strict mode in Powershell. It helps catching typos by preventing access to non-existing properties an uninitialized variables.
Edit
After the updated code, there are still two errors:
# This doesn't make sense. The variable should be SqlParameter, not SqlCommand
$ParamUpdateBackupPathServerName = New-Object -TypeName System.Data.SqlClient.SqlCommand
# Like so:
$ParamUpdateBackupPathServerName = New-Object -TypeName System.Data.SqlClient.SqlParameter
# This is nonsense syntax
$reader = New-Object -TypeName System.Data.SqlClient.SqlDataReader = $UpdateBackupPath.ExecuteReader()
# Like so:
$reader = $UpdateBackupPath.ExecuteReader()