Powershell T-SQL Update Statement Error in Function - sql-server

I'm trying to build function that extracts information about SQL Server Instance and updates a table with the information:
Import-Module "sqlps" -DisableNameChecking
. "D:\V1\CMS\Get-SQLInstance.ps1"
$serverName = 'VIRTSQL120S'
$tableName = 'SQL_SERVER_PRD'
function Update-Table ($srv, $tableName) {
$get_sql_info = Get-SQLInstance -Computername $serverName
foreach ($serverinstance in $get_sql_info) {
$connectionstring=$serverinstance.FullName
$instance=$serverinstance.SQLInstance
$version=$serverinstance.Version
$edition=$serverinstance.edition
$caption=$serverinstance.Caption
$port=$serverinstance.Instance_Port
Invoke-Sqlcmd -ServerInstance 'VIRTSQL152S' -Database 'SQL_info' -Query "
UPDATE $tableName SET instance = '$instance',
connection_name = '$connectionstring',
version = '$version',
edition = '$edition',
caption = '$caption',
port = '$port'
WHERE servername = '$serverName';"
}
}
Update-Table $serverName, $tableName`
The error that I'm getting is:
Invoke-Sqlcmd : Incorrect syntax near the keyword 'SET'.
At line:19 char:13
+ Invoke-Sqlcmd -ServerInstance 'VIRTSQL152S' -Database >'SQL_info' -Qu ...
+ >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Invoke-Sqlcmd], >SqlPowerShellSqlExecutionException
+ FullyQualifiedErrorId : >SqlError,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
If I execute this outside the function it works with no issues. Any help?
Thanks.
I've tried with a query variable and the error is the same:
$query = "UPDATE $tableName SET instance = '$instance',`
connection_name = '$connectionstring',`
version = '$version',`
edition = '$edition',`
caption = '$caption',`
port = '$port'`
WHERE servername = '$serverName';"

Related

Dynamic SQL Where condition, in Powershell script

I am working on a Powershell script where output of query #1 is where condition feed for query #2 but it is not getting the feed, if someone please see and let me know what could be possible solution s for this.
Also please note, in real environment, both the queries are being run on different instances and no possibilities of linked server
Below example is what I was trying in the AdventureWorks database:
$instance="WIN2016-SQL01\SQLSERVER_01"
$database = "AdventureWorks2014"
$query1 = "SELECT TOP 10 [BusinessEntityID] FROM [AdventureWorks2014].[Person].[BusinessEntityAddress] where BusinessEntityID < 10 order by 1 "
$Q1 = (invoke-sqlcmd -query $query1 -ServerInstance $instance -Database $database)
$query2 = "SELECT * FROM [AdventureWorks2014].[Person].[Person] where BusinessEntityID in ($Q1)"
$Q2 = invoke-sqlcmd -query $query2 -ServerInstance $instance -Database $database
Or you can just build a delimited string to substitute for your IN clause:
$instance="localhost"
$database = "AdventureWorks2017"
$query1 = "SELECT TOP 10 [BusinessEntityID] FROM [Person].[BusinessEntityAddress] where BusinessEntityID < 10 order by 1 "
$Q1 = (invoke-sqlcmd -query $query1 -ServerInstance $instance -Database $database)
$ids = ""
foreach ($r in $Q1)
{
$ids += "," + $r.BusinessEntityID
}
$ids = $ids.Substring(1)
$query2 = "SELECT * FROM [Person].[Person] where BusinessEntityID in ($ids)"
$Q2 = invoke-sqlcmd -query $query2 -ServerInstance $instance -Database $database
$Q2 | format-table
The list of values can be passed as an XML parameter to the query, where XML methods can be used to extract the value. A JSON string value is an option in SQL 2016 and later but I see you are on SQL Server 2014.
The example below converts the Q1 result list of BusinessEntityID values to an XML parameter value. Since Invoke-SqlCmd doesn't support parameterized queries, it is necessary to use the SqlClient objects directly. An alternative to Invoke-SqlCmd is Invoke-DbaQuery from dbatools, which supports parameterized queries if you have that avaiable.
$instance="WIN2016-SQL01\SQLSERVER_01"
$database = "AdventureWorks2014"
$query1 = "SELECT TOP 10 [BusinessEntityID] FROM [AdventureWorks2014].[Person].[BusinessEntityAddress] where BusinessEntityID < 10 order by 1 "
$Q1 = (invoke-sqlcmd -query $query1 -ServerInstance $instance -Database $database)
$list = #()
foreach ($row in $Q1)
{
$list += $row["BusinessEntityID"]
}
$listXml = $list | ConvertTo-Xml -NoTypeInformation
$listXmlString = $x.Objects.InnerXml
$query2 = "SELECT *
FROM [AdventureWorks2014].[Person].[Person]
WHERE BusinessEntityID IN (
SELECT item.value('.','int')
FROM #list.nodes('/Object') AS list(item)
);"
$connectionString = "Data Source=$instance;Initial Catalog=$database;Integrated Security=SSPI"
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
$command = New-Object System.Data.SqlClient.SqlCommand($query2, $connection)
$dataAdapter = New-Object System.Data.SqlClient.SqlDataAdapter($command)
($command.Parameters.Add("#list", [System.Data.SqlDbType]::Xml)).Value = $listXmlString
$Q2 = New-Object System.Data.DataTable
$dataAdapter.Fill($Q2)

How to safely update database using powershell syntax?

I have a PowerShell script which runs safely up to the point of an update. I connect to the database and can read from it, however, I have trouble updating.
Here is a copy of the error message I receive:
"Exception calling "ExecuteNonQuery" with "0" argument(s): "There is already an open DataReader associated with this Command which must be closed first."
At C:\xxx\xxx\ToolsController.ps1:37 char:5
+ $InnerCommand.ExecuteNonQuery()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : InvalidOperationException"
FYI: I posted this before and this is an update to the previous question.
Any help at all would be greatly appreciated.
I have tried attaching the variable at the end of the string i.e '..+$varibale'
also tried #" "# and I have gotten the same result.
$Global:Server = "server"
$Global:Database = "db"
$Global:u = "un"
$Global:p = "pw"
[string]$Query = "SELECT * FROM [dbo].[jobs] WHERE [Run] = 0"
try{
$ConnectionString = "server=$Server;Integrated Security=true;database=$Database;user id=$u;password=$p"
$Connection = New-Object System.Data.SqlClient.SqlConnection
$Connection.ConnectionString = $ConnectionString
$Connection.Open()
}
catch{
"Failed to connect SQL Server"
}
$Command = $Connection.CreateCommand()
$InnerCommand = $Connection.CreateCommand()
$Command.CommandText = $Query
$jobs = $Command.ExecuteReader()
while ($jobs.Read()){
switch($jobs["Script"]){
15
{
$traverse = $jobs["Frequency"] - 1
While ($traverse -ge 0) {
c:\xx\xx\batch $jobs["Date"]
$traverse--
}
"Updating Database...."
$Query2 = "UPDATE [dbo].jobs SET [Run] = 1 WHERE [ID] = $jobs['ID']" <------Error here.
$InnerCommand.CommandText = $Query2
$InnerCommand.ExecuteNonQuery()
"Updating Completed!"
}
}
}
$Connection.Close()
$Connection.Dispose()
Write-Host "Connection Closed"
Add MultipleActiveResultSets=True to the connection string. This will allow interleaving of UPDATE statements while reading from the data reader.

Can connect to SQL Server from SSMS from other computer but Can't connect to SQL Server in a network from powershell

i am trying to connect to a sql server from powershell and getting this error
Exception calling "Open" with "0" argument(s): "The target principal
name is incorrect. Cannot generate SSPI context." At
C:\Users\Musawwir\Downloads\remotely access db.ps1:10 char:1
+ $Connection.Open()
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: ( [], MethodInvocationException
+ FullyQualifiedErrorId : SqlException Exception calling "Fill" with "1" argument(s):
I can connect to SQL SERVER from other machines through SSMS but can not connect through powershell, also the script runs fine on local machine.
Here is the code i am using :
[string] $Server= "tcp:DESKTOP-J9UQ90E,1433"
[string] $Database = "Eshop"
[string] $SQLQuery= "[dbo].[usp_getCustomerAge]"
$Connection = New-Object System.Data.SQLClient.SQLConnection
$Connection.ConnectionString = "server='$Server';database='$Database';Connection Timeout=300;Integrated Security=TRUE;UID=ali;PWD=1234"
$Connection.Open()
$Command = New-Object System.Data.SQLClient.SQLCommand
$Command.Connection = $Connection
$Command.CommandText = $SQLQuery
$Command.CommandTimeout=500
$adp = New-Object System.Data.SqlClient.SqlDataAdapter $Command
$data = New-Object System.Data.DataSet
$adp.Fill($data) | Out-Null
$data2 = ''
$emp = ''
foreach($Row in $data.Tables.Rows){
[string]$C_name = $Row[0]
$C_name
$data2= $data2+ "|"+$C_name
$C_name = $C_name+"|"
$C_name | out-file "d:\\test4.txt" -Append
}
$Connection.Close()
#$data2 | out-file "d:\\test4.txt"
The user ali wasn't having access to stored procedure .. changed user from ali to sa and integrated security to false solved the problem

Exception calling "ExecuteReader" with "0" argument(s): "Incorrect syntax near '='."

I am writing a powershell code with an SQL query to get information from one of our Databases and then put that information into a variable for further use.
My entire code works, and does exactly what i want it to do.
But somewhere in this snippet of the code two errors are produced that I can't figure out why happen if the script is actually doing what it's supposed to do.
$Server = "######"
$Database = "######"
$UserSqlQuery = "SELECT [UdstyrsId]
,[Model]
,[Serienr]
,[Udlevdato]
,[Repnr]
,[Notat]
FROM [IT-Support].[dbo].[Udstyrsoplysninger] where aflevdato is null and repnr = $repnr"
function ExecuteSqlQuery($Server, $Database, $UserSQLQuery)
{
$Datatable = New-Object System.Data.DataTable
$Connection = New-Object System.Data.SQLClient.SQLConnection
$Connection.ConnectionString = "server='$Server';database='$Database';integrated security=true;"
$Connection.Open()
$Command = New-Object System.Data.SQLClient.SQLCommand
$Command.Connection = $Connection
$Command.CommandText = $UserSQLQuery
$Reader = $Command.ExecuteReader();
$Datatable.Load($Reader);
$Connection.Close()
return $Datatable
}
$ResultsDataTable = New-Object System.Data.DataTable
$ResultsDataTable = ExecuteSqlQuery $Server $Database $UserSqlQuery
Error code:
Exception calling "ExecuteReader" with "0" argument(s): "Incorrect syntax near '='."
At C:\Users\adm-#######\Desktop\UserResignation.ps1:70 char:13
+ $Reader = $Command.ExecuteReader();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : SqlException
Exception calling "Load" with "1" argument(s): "Value cannot be null.
Parameter name: dataReader"
At C:\Users\adm-#######\Desktop\UserResignation.ps1:71 char:13
+ $Datatable.Load($Reader);
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentNullException
Line 70 and 71 is these lines in the script:
$Reader = $Command.ExecuteReader();
$Datatable.Load($Reader);
You forgot to declare $repnr for query's where condition and because of that you get syntax error where aflevdato is null and repnr = .
$repnr = 123
$UserSqlQuery = "SELECT [UdstyrsId]
,[Model]
,[Serienr]
,[Udlevdato]
,[Repnr]
,[Notat]
FROM [IT-Support].[dbo].[Udstyrsoplysninger] where aflevdato is null and repnr = $repnr"

SQL query for Bulk Update

I need to update a table using text file. Currently my code works fine if I perform Get-Content from txt file and then run the SQL update query, but only in case of small data. If the size of text is too long or it contains some special characters, it throws an error as following:
Exception calling "ExecuteReader" with "0" argument(s): "Incorrect syntax near
')</td><td style=\"border:1px solid #cccccc\">#fieldValueEmpty($issue.getCustom
FieldValue($componentTypeCf),'."
At C:\Users\d-mansings\Desktop\Scripted Field Configuration\Script\Prod_UpdateS
cript.ps1:78 char:37
+ $Reader = $Command.ExecuteReader <<<< ()
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
Following is the code I'm using:
Function DatabaseQueries(){
#To connect to the SQL database
$Connection = New-Object System.Data.SQLClient.SQLConnection
$Connection.ConnectionString = "Server=$IPSource ; Database=$DBNameSource ; User ID=$UserIDSource ; Password=$LoginPwdSource;"
$Connection.Open()
#Query to get the ID of the stored script field from propertyentry
$Command1 = New-Object System.Data.SQLClient.SQLCommand
$Command1.Connection = $Connection
$Command1.CommandText = "SELECT [ID] FROM [dbo].[propertyentry] WHERE [PROPERTY_KEY]='com.onresolve.jira.groovy.groovyrunner:customfields' "
$Reader = $Command1.ExecuteReader()
while ($Reader.Read()) {
$ID = $Reader.GetValue($1)
}
#To get the updated script file
$ScriptDir = $ParentDir + '\Script.txt'
$ScriptData = Get-Content "$ScriptDir"
$Connection.Close()
#Query to update the Script in JIRA database
$Connection.Open()
$Command = New-Object System.Data.SQLClient.SQLCommand
$Command.Connection = $Connection
$Command.CommandText = #"
Update [dbo].[propertytext] set [propertyvalue] ='$ScriptData' Where ID=$ID
"#
$Reader = $Command.ExecuteReader()
$Connection.Close()
}
It is difficult to write a complete solution if file contents and database structure are not specified. You surely encountered some kind of SQL injection. SQL Query concatenation is considered harmful and you should avoid it. Use ADO.NET parameters to pass variables ($Command.Parameters.AddWithValue in your example). See the following example:
function Invoke-Sql(
$ConnectionString,
$Query,
$Parameters
) {
$conn = New-Object System.Data.SqlClient.SqlConnection -ArgumentList $ConnectionString
$cmd = New-Object System.Data.SqlClient.SqlCommand -ArgumentList $Query,$conn
$conn.Open()
foreach ($arg in $Parameters.GetEnumerator()){
$cmd.Parameters.AddWithValue($arg.Key, $arg.Value) | Out-Null;
}
$reader = $cmd.ExecuteReader()
if ($reader.Read()) {
[string[]]$columns = 0..($reader.FieldCount-1) |
% { if ($reader.GetName($_)) { $reader.GetName($_) } else { "(no name $_)" } }
do {
$obj = #{}
0..($reader.FieldCount-1) | % { $obj.Add($columns[$_], $reader[$_]) }
New-Object PSObject -Property $obj
} while ($reader.Read())
}
$reader.Dispose()
$cmd.Dispose()
$conn.Dispose()
}
Invoke-Sql `
-ConnectionString "Server=.\SQL2014;Database=Test1;Integrated Security=true" `
-Query 'SELECT Name, Id [ObjectId], Id + 3, #arg FROM IdNameTest' `
-Parameters #{arg = 'Some text'''}
Invoke-Sql `
-ConnectionString "Server=.\SQL2014;Database=Test1;Integrated Security=true" `
-Query 'UPDATE IdNameTest SET Name=#name WHERE Id=#id' `
-Parameters #{name = "'DROP DATABASE Death;! %&#!$"; id=1}
Thanks for the response, I have figured out a way to execute the query by just using a replace function, as it was getting confused between the single inverted commas
select REPLACE(Cast(propertyvalue AS varchar(Max)), '''', '''''') FROM [dbo].[propertytext] WHERE ID=$ID

Resources