Error when inserting strings with $( with Invoke-SqlCmd in Powershell - sql-server

Setup:
CREATE TABLE MyTest (TestCol1 nchar(5))
Test:
Following work:
Invoke-Sqlcmd -Database "databasename" -ServerInstance "hostname" -OutputSqlErrors $True -Query "INSERT INTO MyTest VALUES ('`$5')"
Invoke-Sqlcmd -Database "databasename" -ServerInstance "hostname" -OutputSqlErrors $True -Query "INSERT INTO MyTest VALUES ('(5')"
Following fails with the error below:
Invoke-Sqlcmd -Database "databasename" -ServerInstance "hostname" -OutputSqlErrors $True -Query "INSERT INTO MyTest VALUES ('`$(5')"
Invoke-Sqlcmd -Database "databasename" -ServerInstance "hostname" -OutputSqlErrors $True -Query "INSERT INTO MyTest VALUES ('`$`(5')"
Error:
Invoke-Sqlcmd :
At line:1 char:1
+ Invoke-Sqlcmd -Database "databasename" -ServerInstance "hostname" -Ou ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ParserError: (:) [Invoke-Sqlcmd], ParserException
+ FullyQualifiedErrorId : ExecutionFailureException,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
Aftersome research I found that $( provides functionality in powershell thus is reserved. However I tried escaping the parenthesis but with no success. I've tried finding alternatative. Any ideas on how I can do this? If I use the CHAR function in SQL Server that works but it would be a pain to deal with in my code. Thank you.

All you need to do is disable the variables lookup in the invoke-Sqlcmd by adding -DisableVariables.
$() is use to pass in variable into the sql statements.
Invoke-Sqlcmd -InputFile $fileName -ServerInstance $serverInstance -DisableVariables
Source http://msdn.microsoft.com/en-us/library/cc281720.aspx

try this:
'INSERT INTO MyTest VALUES ("$(5")'
or this:
'INSERT INTO MyTest VALUES (''$(5'')'

The $(anyvalue) are reserved variables to be used with SQLCMD.EXE
Change the symbol $ by the sql server CHAR(36):
Invoke-Sqlcmd -Database "databasename" -ServerInstance "hostname" -OutputSqlErrors $True -Query "INSERT INTO MyTest VALUES (CHAR(36)+'(5)')"

The issue seems that the $( is a reserved word in Powershell thus to make this work I had use the CHAR function to store the $ (CHAR(36)). Once I did this it worked but for this project I abandoned using the Invoke-SqlCmd command and used a standard ADO.NET method. It seems that the Invoke-SqlCmd wants to parse the command and for the reserved combination continues to see the reserved word even if you escape the characters.

"INSERT INTO MyTest VALUES ('`$5')"
Outputs a string
INSERT INTO MyTest VALUES ('$5')
"INSERT INTO MyTest VALUES ('(5')"
Outputs a string
INSERT INTO MyTest VALUES ('(5')
"INSERT INTO MyTest VALUES ('`$(5')"
Outputs a string
INSERT INTO MyTest VALUES ('$(5')
"INSERT INTO MyTest VALUES ('`$`(5')"
Outputs a string
INSERT INTO MyTest VALUES ('$(5')
What do you want to insert? Actually '$5' or the value of the variable $5? Also, you have strange parentheses around your variable $5. Not sure if is suppose to be like that or your don't understand another important part of powershell variables? Because this a variable in a string "There are $((Get-Process).Count) processes running" too.

Related

Connecting powershell to SQL Server

I'm quite new to both SQL and Power shell, but I want to run a script which pulls data from a server within SQL - Any advice on where to start?
I tried using this as a starting point, but got no luck as it doesn't like my credentials even though they are correct
Test:
SqlConnection -ServerName 'END-HDSQ02\DEV4' -DatabaseName 'tbl_cert_expiry' -Credential (Get-Credential)
EDIT: Since Powershell V2, you are required to manually load the necessary Snap-Ins;
Powershell - Invoke-Sqlcmd unable to run
Add-PSSnapin SqlServerCmdletSnapin100
Add-PSSnapin SqlServerProviderSnapin100
This is what we use to query a SQL View;
[string] $Server= "ServerName"
[string] $Database = "DatabaseName"
[string] $SQLQuery= $("SELECT * FROM schema.TableView order by column")
$data = Invoke-Sqlcmd -ServerInstance $server -Database $database -Username $username -Password $value1 -Query $SQLQuery
Obviously you'll need to pass the connecting user and password as well, but I've omitted them from my example.

Powershell sql insert error data will be truncated removed data and still get error

I have a powershell script that I am trying to use to write data from a source .txt to an SQL table
Import-Csv $_.fullname -Header Z_AP_EXNUM,Z_AP_ID | ForEach-Object {Invoke-Sqlcmd `
-Database $database -ServerInstance $server -Username $uid -Password $pwd `
-Query "insert into $table VALUES ('$_.Z_AP_EXNUM','$_.Z_AP_ID')"
My actual code has about 293 headers/columns to insert. The error I receiving is that String or binary data will be truncated. The statement has been terminated. I went into one of my test files and removed all the data but a few standard fields and still receive these errors. I am not sure what could be causing the insert to fail.
Error:
Invoke-Sqlcmd : String or binary data would be truncated.
The statement has been terminated.
At C:\Users***\Desktop\script\scripy.ps1:79 char:111
+ Z_AP_STR_CSEM5_9,Z_AP_STR_NAME5_10,Z_AP_STR_SCOR5_10,Z_AP_STR_CSEM5_10 | ForEach-Object {Invoke-Sqlcmd <<<< `
+ CategoryInfo : InvalidOperation: (:) [Invoke-Sqlcmd], SqlPowerShellSqlExecutionException
+ FullyQualifiedErrorId : SqlError,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
Check the table schema, this error usually shows due to the difference of column definition type in the table and the powershell data type you want to store in it.

Error passing variable SQL instance name into Invoke-SQLcmd

I'm working on a project that pulls a list of SQL instances from server A and then loops through the returned list and runs a query (eventually to audit users and insert results into table) but getting an error instance not found. It seems I'm not defining the variable correctly in the loop because if I hardcode the instance name it works.
I appreciate any input on how to fix this.
$Serverlist = invoke-sqlcmd -ServerInstance TESTSERVER1 -Database TESTDB -Query "SELECT instancename from testtable"
foreach ($SQLInst in $Serverlist)
{
$Inst = $SQLInst.INSTANCE
Invoke-Sqlcmd -ServerInstance ${$Inst} -Database Master -Query "select ##servername as servername" | select -ExpandProperty servername
} #end foreach loop
Invoke-Sqlcmd : A network-related or instance-specific error occurred
while establishing a connection to SQL Server. The server was not
found or was not accessible. Verify that the instance name is correct
and that SQL Server is configured to allow remote connections.
(provider: Named Pipes Provider, error: 40 - Could not open a
connection to SQL Server) At line:12 char:1
+ Invoke-Sqlcmd -ServerInstance ${$SQLInst} -Database Master -Query "select ##serv ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Invoke-Sqlcmd], SqlException
+ FullyQualifiedErrorId : SqlExectionError,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
There's no reason to use curly braces like that ${$Inst} in this instance.
Simply using $Inst will work fine. If you do use curly braces, then you don't use the $ inside: ${Inst}.
Invoke-Sqlcmd -ServerInstance $Inst
# or
Invoke-Sqlcmd -ServerInstance ${Inst}
I would check to make sure that each instance is the correct one:
$Serverlist = invoke-sqlcmd -ServerInstance TESTSERVER1 -Database TESTDB -Query "SELECT instancename from testtable"
foreach ($SQLInst in $Serverlist)
{
$Inst = $SQLInst.INSTANCE
Write-Host $Inst
} #end foreach loop
I noticed some problems with my previous statement. Can you try this?
$Serverlist = invoke-sqlcmd -ServerInstance TESTSERVER1 -Database TESTDB -Query "SELECT instancename from testtable"
foreach ($SQLInst in $Serverlist)
{
$Inst = $SQLInst.instancename
Invoke-Sqlcmd -ServerInstance "$Inst" -Database Master -Query "select ##servername as servername" | select -ExpandProperty servername
} #end foreach loop
When you are referencing a result from a query, you must specify the column name even if there is only one column in the query. Enclosing the query in parentheses and using dot notation with the column name will convert the rows to a list of values. Your original problem was you had the column name incorrect.
Try this:
$Serverlist = ( invoke-sqlcmd -ServerInstance TESTSERVER1 -Database TESTDB -Query "SELECT instancename from testtable").instancename
$Serverlist | ForEach-Object {
( Invoke-Sqlcmd -ServerInstance $SQLInst -Database Master -Query 'select ##servername as servername' ).servername
}

Load text into SQL Server via Powershell

I am loading character data read from a file row by row into a SQL table. The table is:
CREATE TABLE [dbo].[PSFileOrder](
[PSFOrder_Id] [int] IDENTITY(0,1) NOT NULL,
[PSFile] [varchar](255) NOT NULL,
CONSTRAINT [PK_PSFileOrder] PRIMARY KEY CLUSTERED
(
[PSFOrder_Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
The powershell code I am using is
#LoadPSFile
$PSFile = "d:\ps\xx.ps1"
cls
$xy = Get-content $PSFile
$xy
foreach($xy in $xy) {invoke-sqlcmd -serverInstance localhost\sqlexpress -query "Insert AA.dbo.PSFileOrder(PSFile) Values ('$xy')"}
If the file I am loading is:
#Filename
#The first line will always be the file name
cls
$filter = "*.*"
$folder = "\\localhost\d$\Master\men and their toys"
$filecount = (Get-ChildItem -literalpath $folder -filter $filter).Countem -literalpath $folder -filter $filter).Countem -literalpath $folder -filter $filter).Countem -literalpath $folder -filter $filter).Count
If ($filecount -lt 1) {$filecount = 0}
"There are {0} files in the {1} directory" -f $filecount, $folder
the file loads without error. If there is a single quote in the text
"There are {0} files in the {1} d'irectory" -f $filecount, $folder
then I will recieve this error
Invoke-Sqlcmd : Incorrect syntax near 'irectory'.
Unclosed quotation mark after the character string ' -f $filecount, $folder')
;'.
At C:\Users\RC\AppData\Local\Temp\2ed13f21-5b46-4df4-a5ee-2488c3dd5ee4.ps1:6 char:35
+ foreach($xy in $xy) {invoke-sqlcmd <<<< -serverInstance localhost\sqlexpress -query "Insert AA.dbo.PSFileOrder(PSFile) Values ('$xy')"}
+ CategoryInfo : InvalidOperation: (:) [Invoke-Sqlcmd], SqlPowerShellSqlExecutionException
+ FullyQualifiedErrorId : SqlError,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
I do believe the error is caused by the way I have written the "-query" statement using single quotes arount the $xy variable.
My questions are:
Have I crafted the Invoke-sqlcmd correctly?
Is there a substitue for the single quote around $xy?
Is there a better way to load the text?
I want to maintain the one to one relationship of the text file and row in the db.
Thanks
RC
Two answers put me on the correct path of doubling the single quote prior to loading to SQL. The revised code is
#LoadPSFile
cls
Get-content "d:\ps\xx.ps1" |
Foreach-Object {$_ -replace "'", "''"} |
ForEach-Object{invoke-sqlcmd -serverInstance localhost\sqlexpress -query "Insert AA.dbo.PSFileOrder(PSFile) Values ('$_')"}
You get malformed SQL syntax. I believe the quick fix is to replace any single quote with two single quotes in $xy:
$escaped = $xy.Replace("'", "''")
invoke-sqlcmd -serverInstance localhost\sqlexpress -query "Insert AA.dbo.PSFileOrder(PSFile) Values ('$escaped')"
# or (the same but without the intermediate variable):
invoke-sqlcmd -serverInstance localhost\sqlexpress -query #"
Insert AA.dbo.PSFileOrder(PSFile) Values ('$($xy.Replace("'", "''"))')
"#
But this is perhaps not the best way. Unfortunately I am not familiar with invoke-sqlcmd capabilities. If it supports parameterized queries I would go that way and supply $xy value as it is as a parameter value (no preparation of $xy would be needed in such a case).
You must double the apostrophes within the string
foreach($xy in $xy)
{
$xy = $xy -replace "'", "''"
invoke-sqlcmd -serverInstance localhost\sqlexpress -query "Insert AA.dbo.PSFileOrder(PSFile) Values ('$xy')"
}
when performance is an issue, you choose some bulkcopy solultion. In the SQLPSX project
there is a module adolib.psm1 which contains a function invoke-bulkcopy
link text
A better solution would be to use ADO.NET paramaterized queries via the SqlCommand.Parameters property. This will solve your single apostrophe error as well as scrub your input for anything else that could create a sql error.
Unfortunately Invoke-SqlCmd does not allow you to do this. However, I modified Chad Miller's Invoke-SqlCmd2 to allow you to do this. Include Invoke-SqlCmd2.ps1 in your profile and change your script like so:
foreach($xy in $xy) {invoke-sqlcmd -serverInstance localhost\sqlexpress -query "Insert AA.dbo.PSFileOrder(PSFile) Values (#xy)" -SqlParamaters #{ '#xy' = $xy}}

Invoke-Sqlcmd cmdlet throws exception when using -Variable parameter

When I try to use the Invoke-Sqlcmd cmdlet from SQL Server 2008 to execute a query that contains scripting variables, ex. $(MyVar), I receive the following exception:
Invoke-Sqlcmd : Object reference not set to an instance of an object.
Here's the code I'm trying to run (which is copy/paste from the Books Online example with only the connection parameters added).
$MyArray = "MyVar1 = 'String1'", "MyVar2 = 'String2'"
Invoke-Sqlcmd -Query "SELECT `$(MyVar1) AS Var1, `$(MyVar2) AS Var2;" -Variable $MyArray -ServerInstance "localhost" -Database "master" -UserName "who" -Password "me"
If I replace $(MyVar1) and $(MyVar2) in the -Query with 'x' and 'y' then it runs perfectly.
$MyArray = "MyVar1 = 'String1'", "MyVar2 = 'String2'"
Invoke-Sqlcmd -Query "SELECT 'x' AS Var1, 'y' AS Var2;" -Variable $MyArray -ServerInstance "localhost" -Database "master" -UserName "who" -Password "me"
Can anyone tell me why this is not working?
Indeed this is a bug in SQL Server - tracked and fixed here
https://connect.microsoft.com/sqlserver/feedback/details/358291/invoke-sqlcmd-powershell-cmdlet-fails-when-array-passed-via-variable
However, there's a posted workaround. Remove the spaces around the assignment. So instead of
$MyArray = "MyVar1 = 'String1'", "MyVar2 = 'String2'"
use
$MyArray = "MyVar1='String1'", "MyVar2='String2'"
Ok. I posted this same question on the SQL Server forums and, apparently, this is a bug in SQL Server 2008's PowerShell cmdlets... follow the thread here.
Try this alone:
PS>$MyArray = "MyVar1 = 'String1'", "MyVar2 = 'String2'"
Now:
PS>$MyArray
and
PS>MyVar1
Now:
PS>$MyArray|get-member
PowerShell thinks you've assigned 2 string objects to $MyArray, nothing more. This approach does not result in defining the variables $MyVar1 and $MyVar2 to PowerShell.
Sorry, I can't fire up my SQL2008 VM right now to comment on the other parts...

Resources