Replace Single Quotes in PowerShell Or Excel CSV - sql

I'm wrapping up a script which gets software versions and puts them into a CSV, then back to powershell then finally to SQL. Some of the software names have single quotes in them which is not allowed for import to SQL. I'm trying to find and replace these single quotes with back ticks at some point before the final SQL export. My first instinct is to operate the find and replace function in excel via powershell to do the replace, but I'm not sure how to go about this or if this is the best way.
Any advice is greatly appreciated. I'm very new to all this.
Thanks
edit: Thanks for advice so far alroc. I'm working with the following for this piece:
##SQL PART##
$CSV = Import-CSV -path $UpdatePath\$($todaydate).csv
import-module sqlps
ForEach ($item in $CSV){
$CSVAppID = $($item.AppID)
$CSVAppName = $($item.AppName)
$CSVVersion = $($item.Version)
$SqlServer = "redacted"
$SqlDB = "redacted"
$SoftwareTable = "redacted"
Invoke-Sqlcmd -ServerInstance "$SQLServer" -Database "$SqlDB" -query "INSERT INTO dbo.ecca_sw_standard_2 (name, version, product_id) VALUES (N'$CSVAppName', N'$CSVVersion' , N'$CSVAppID')"
}
A few of the variable values contain single quotes in the strings, and powershell throws me an error when it gets to these few. "Invoke-Sqlcmd : Incorrect syntax near 'S'."

The answer is to create sub expressions in your string to replace ' with ''.
Invoke-Sqlcmd -ServerInstance "$SQLServer" -Database "$SqlDB" -query "INSERT INTO dbo.ecca_sw_standard_2 (name, version, product_id) VALUES (N'$($CSVAppName -replace "'", "''")', N'$($CSVVersion -replace "'", "''")' , N'$($CSVAppID -replace "'", "''")')"
This way when the string is expanded it will be appropriately escaped for SQL insertion.

Related

Japanese Characters are not recognized when using Invoke-SqlCmd -Query

I'm inserting data from a json file to an SQL server table using Invoke-SqlCmd and using a stored procedure as following:
Invoke-SqlCmd -ServerInstance $servername -Database $database -Query "EXEC dbo.InsertDataFromJson #JSON='$json'
The json is obtained by getting it's raw content:
$json = Get-Content -Path "path\to.json" -Raw
$json # Content:
'{"Id": "2fe2353a-ddd7-479a-aa1a-9c2860680477",
"RecordType": 20,
"CreationTime": "2021-02-14T08:32:23Z",
"Operation": "ViewDashboard",
"UserKey": "10099",
"Workload": "PowerBI",
"UserId": "102273335#gmail.com",
"ItemName": "テスト",
"WorkSpaceName": "My Workspace",
"DashboardName": "テスト",
"ObjectId": "テスト" }'
All the column with strings, emails and japanese characters are NVARCHAR(MAX).
The problem is my json contains Japanese characters and they appear as ???? in the table.
When I try to insert a sample using SSMS directly it works fine.
Do you have any idea how to fix this ?
Thank you
Try setting the -Encoding flag to Utf8.
{"test":"みんな"}
Get-Content -Path ".\test.json" -Encoding Utf8
I just found an elegant solution to this mess, if you ever encounter the same problem.
First, I have a stored procedure that takes a parameter. The website that helped is: https://community.idera.com/database-tools/powershell/ask_the_experts/f/sql_server__sharepoint-9/18939/examples-running-sql-stored-procedures-from-powershell-with-output-parameters
Instead of using Invoke-SqlCmd (which is the worst), I used System.Data.SqlClient.SqlCommand as follow:
$connection.ConnectionString="Server={0};Database={1};Integrated Security=True" -f $servername, $database
$connection.Open()
Here I use Integrated Security so I don't need to enter my creds. "dbo.InsertDataFromJson" is my stored procedure.
$Command = new-Object System.Data.SqlClient.SqlCommand("dbo.InsertDataFromJson", $connection)
$json = Get-Content -Path .\sample.json -Raw
$Command.Parameters.Add("#JSON", [System.Data.SqlDbType]"NVARCHAR")
$Command.Parameters["#JSON"].Value = $json
$Command.ExecuteScalar()
$connection.Close()
And Voilà! My japanese characters are there, everything is fine and I'm very happy :)

How to return multiple recordsets from stored procedure using PowerShell

I need to run a stored procedure that return 2 result sets with PowerShell. I use dbatools to do so but I could use .NET to get there. I just don't know how.
For this example, I use exec sp_spaceused that will return the space used in the actual database. Here's the result in SSMS:
As you can see here, there are 2 result sets. Now when I run the same command in PowerShell, I can't figure how to get the next result set.
Here is the code I've come up with:
$conn = Connect-DbaInstance -SqlInstance . -MultipleActiveResultSets
$query = 'exec sp_spaceused'
Invoke-DbaQuery -SqlInstance $conn -Query $query
I'm not even sure if I used MultipleActiveResultSets in the right way. I can't find any good example anywhere.
Wow, I just found the answer by testing all the different -As options. Here's the code:
$conn = Connect-DbaInstance -SqlInstance . -Database 'StackOverFlow'
$query = 'exec sp_spaceused'
$ds = Invoke-DbaQuery -SqlInstance $conn -Query $query -As DataSet
foreach ($table in $ds.Tables) {
$table | Out-String
}
I use Out-String to avoid joining objet but you could use Out-GridView. I also realize that I don't need to use -MultipleActiveResultSets.

SQL Update Statement in Powershell Issue with Website Escape Charactor

I am trying to Iterate through a CSV file to do an Update Query in SQL. The CSV has Websites, a nickname for a site, and a resource group. It is having issues reading the website because it contains / which I think is an escape charactor. Is there a way to format WHERE SITES = $($c.sites) so it doesnt throw the following error? Invoke-SQLcmd : Incorrect syntax near 'https:'. The sites look like https://fakesite.com/something
foreach($c in $csv){
$updatequery="
UPDATE [dbo].[Table]
SET SiteName = $($c.sitename), RSG_Name= $($c.Resource_Group)
WHERE SITES = $($c.sites)
GO"}
Invoke-SQLcmd -ServerInstance 'serverinstance' -query $updatequery -Database db```

How to add value comma-separated parameter calling SQL query using SQLCMD via Powershell

I'm writing a PowerShell script that makes a call with SQLCMD to execute a .sql file and output a CSV file. In SQLCMD command, I want to pass a parameter back to query that includes a value of comma-separated list so I can use in an 'IN' statement.
select a, b, c, d
from dbtbl (nolock)
where client_id in ($(clients))
The SQLCMD command works if I assign $client_id to a singular value i.e. '000_abc' but does not work if I assign the variable as an array. i.e. '000_abc','000_def', '000_ghi'
# Input variables
$SQLSourceFolder = "V:\PS\queries\"
$FolderFile = "V:\PS\Myoutput.csv"
$TaxCode = "MY*script*.sql"
$qtr = "'1'"
$year = "'2019'"
#Works Fine. Only one client filtered in SQL query where clause
$client_id = "'000_abc'"
#Doesn't Work. Multiple clients filtered in SQL query where clause with IN statement.
# $client_id = "'000_abc','000_def', '000_ghi'"
# Find all files matching $TaxCode filter in the folder specified
Get-ChildItem -Path $SQLSourceFolder -Filter $TaxCode | ForEach-Object {
cls
Start-Process "D:\Program Files\Microsoft SQL Server\Client SDK\ODBC\110\Tools\Binn\SQLCMD.EXE" -ArgumentList #("-S","tcp:XXXXXXXXXXXXXX,1433", "-d" ,"XXX", "-U", "XXXXXXXX", "-P" ,"XXXXXXXXXX", " -v", " qtr = $qtr", " yr = $year", " clients = $client_id", " -W ", "-s", "~", "-i", "$($_.FullName)", "-o", "$FolderFile")
}
The expected result would be to pass multi-value variable to sql query where parameter is used in where clause with IN statement.
How come you're using sqlcmd.exe instead of Invoke-SqlCmd or, better yet, Invoke-SqlCmd2?
Quick context: Invoke-SqlCmd2 is a community drop-in replacement for Invoke-SqlCmd which addresses a few issues. It's widely considered the best solution for querying MS SQL from PS. Relevant: Install-Module Invoke-SqlCmd2, https://github.com/sqlcollaborative/Invoke-SqlCmd2
As you are discovering, passing arguments in to native applications is surprisingly difficult. It's always better to use PS commands where possible, in my experience.
Let me know if there's a good reason not to use Invoke-SqlCmd2 and I'll see what I can do to mock this for you, but it's a non-trivial effort on my part, so I do encourage you to just follow normal practice!

Invoke multiple sql using powershell in sequence with runtime arguments

I'm trying to run sql queries in sequence. If any one of the sql query fails, then the windows powershell script should exit and send email. The log should be written to the log directory. where data= < this will passed in the run time>
Example code below:
Invoke-Sqlcmd -Query "SELECT data from emp where data=<run time argument>;" -ServerInstance "MyComputer\MyInstance"
Invoke-Sqlcmd -Query "SELECT data from class where data=<run time argument>;" -ServerInstance "MyComputer\MyInstance"
Invoke-Sqlcmd -Query "SELECT data from stud where data=<run time argument>;" -ServerInstance "MyComputer\MyInstance"
Invoke-Sqlcmd -Query "SELECT data from cust where data=<run time argument>;" -ServerInstance "MyComputer\MyInstance"
Invoke-Sqlcmd -Query "SELECT data from new where data=<run time argument>;" -ServerInstance "MyComputer\MyInstance"
Any help would be appreciated.
Regards,
What does it look like when "sql query fails"? You could rely on the Invoke-SqlCmd function's return, or have an expected "fail" message (or multiple messages).
I'm not familiar with Invoke-SqlCmd. Check out the MSDN page; -AbortOnError looks like it would be helpful to you, as would -ErrorLevel.
Here is an outline for a single expected error, with comment on how to extend. it's worth storing your queries in an array so that you can loop over then and break out of a loop instead of having linear code (where you have to copy and paste the check part after each invoke-sqlcmd
# string with a single error. you could use an array and add
# a foreach ($error in $errors) on the line marked #here
$expectedError = "Failed"
# Functions have to appear above where they are used
Function Check-SQLResults($result){
# a try-catch statement will execute the code in the try part, going
# to the catach part on a TERMINATING error
try{
# check each line for your expected error
foreach($line in $result){
#here
if($line -like "*$expectedError*"){
Write-Error "Something went wrong: $line" -ErrorAction Stop
}
}
# true is only returned if none of the result lines are like your error
return $true
}catch{
# false is returned if any lines contain error
return $false
}
}
# store the sql outcome in a variable so you can check it
$result = Invoke-Sqlcmd -Query "SELECT data from emp where data=;" -ServerInstance "MyComputer\MyInstance"
# using a function that tells you if the results contain an error or not is neater.
# again, this is manually dealing with errors and invoke-sqlcmd provides other options.
$resultIsErrorFree = Check-SQLResults -result $result
If(resultIsErrorFree -eq $true){
# execute next invoke-sqlcmd
}else{
# Send e-mail. $results can be added to body.
}