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

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 :)

Related

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.

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.
}

Powershell Job within SQL Server Agent

I am having trouble with a powershell job returning a result set when it runs, it runs successfully but no results. I am running it against a list of servers. If I run the script against a specific server that I know I can connect to, it runs fine and gives results.
This is a script within the sql server agent running powershell. Am I using the SMO object right? Ive tried to use a try/catch (job fails), ive tried to add -ErrorAction "Continue" to the script (job fails), using smo to resolve a server name in order to use the if statement (job succeeds) but no results. Here is the script:
$ErrorActionPreference = "Continue";
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
$instanceNameList = Get-Content "c:\Scripts\InPutFiles\servers.txt";
$results = #()
foreach($instanceName in $instanceNameList)
{
$serverObject = New-Object Microsoft.SqlServer.Management.Smo.Server($instanceName)
$serverName = $serverObject.ComputerNamePhysicalNetBIOS;
if($serverName -ne $null) {
$results += Invoke-Sqlcmd -Query "
(My Query is in here!)
" -ServerInstance $instanceName.Name}
$instanceName| Where-Object {$_} | Export-csv 'C:\scripts\HungJobs_UnabletoConnect.csv' -NoTypeInformation
}
$results| Where-Object {$_} | Export-csv 'C:\scripts\HungJobs.csv' -NoTypeInformation
Mid-way through your script, is the $scripts variable being populated properly by the Invoke-Sqlcmd?
If so, I'm thinking that you should be able to do away with Where-Object {$_} on your last two commands. Where-Object is used as a filter, and in these two cases it doesn't seem to be filtering anything. You should just be able to directly export the $instanceName and $results directly to csv.

Replace Single Quotes in PowerShell Or Excel CSV

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.

Exporting data onto a csv file or output via email using array

I have a powershell script which I have written, it also works as well. The problem that I now have is that originally the requirement was to save the results onto a database, now I want to email the results as well. I thought about a couple of options, but finding it difficult, now an easy way out which i thought of was to export the result to CSV then attach that CSV to the email.
The code below is inside my loop.
$sql = " INSERT INTO dbo.tb_checks ([ServerName],[Directory],[DirectoryFile] ,[FileCreationDate]) SELECT '$ServerName', '$Filepath', '$fileName', '$FileDate'"
Invoke-Sqlcmd2 -serverinstance $DBServer -database $Database -query $sql
SELECT '$ServerName', '$Filepath', '$fileName', '$FileDate' | Export-csv $Outfilename -append
The CSV file gets generated, but with no data.
Another idea which i thought of was to have the data stored in an array, then loop through/spit out the entire content of the array in an email.
Can someone help please ?
The reason that your CSV is empty is because you aren't feeding it an array that it can work with. What headers would it use in your script? It has no idea, it's just having random stuff thrown at it. Change that last line to this:
New-Object PSObject -Property #{Server=$ServerName;FilePath=$Filepath;FileName=$fileName;FileDate=$FileDate} | Export-csv $Outfilename -Append -NoTypeInformation
Assuming that your variables are set right it should output the file you want.
If you want to make it a table and put it in an email make an empty array before your loop, then do something like:
$LoopArray = #()
<start of loop>
$LoopArray += New-Object PSObject -Property #{Server=$ServerName;FilePath=$Filepath;FileName=$fileName;FileDate=$FileDate}
$LoopArray | Export-csv $Outfilename -Append -NoTypeInformation
<end of loop>
Then afterwards you have the array to work with that has all your data in the CSV stored and can be injected into an email.