Export from SQL Server to json text file using PowerShell - json

You should read this if: you are trying to find out how to transform SQL server data into JSON and put it into a text .json file
Question:
Can someone tell me what's wrong with this code? My goal is to read data from a SQL Server table, convert it to JSON and then save the result as a JSON text file. The code runs but the resulting .json file just has:
{
"FieldCount": 11
},
{
repeated over and over again and nothing more.
My code:
$instance = "localhost\SQLEXPRESS"
$connectionString = "Server=$Instance; Database=myDB;Integrated Security=True;"
$query = "Select * from myTable"
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
$command = $connection.CreateCommand()
$command.CommandText = $query
$result = $command.ExecuteReader()
$result | ConvertTo-Json | Out-File "file.json"
$connection.Close()
Update:
Will award the answer to postanote as technically he/she answered my original question (although I will caveat and say I have not tried it).
However I would recommend either Mike's answer or what I eventually ended up going with, using BCP:
bcp "select * from myTable FOR JSON AUTO" queryout "C:\filepath\testsml.json" -c -S ".\SQLEXPRESS" -d myDBName -T
Note that the JSON AUTO will automatically come up with a json scehma for you vs. JSON pipeline which allows you to customize it.
You have to install BCP first:
https://learn.microsoft.com/en-us/sql/tools/bcp-utility?view=sql-server-2017

If you are using sql server express 2016 or later you should be able to do it on the database side using FOR JSON clause. Try something like
$instance = "localhost\SQLEXPRESS"
$connectionString = "Server=$Instance; Database=myDB;Integrated Security=True;"
$query = "Select * from myTable FOR JSON AUTO"
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
$command = $connection.CreateCommand()
$command.CommandText = $query
$command.ExecuteScalar() | Out-File "file.json"

Try something like this …
### Exporting SQL Server table to JSON
Clear-Host
#--Establishing connection to SQL Server --#
$InstanceName = "."
$connectionString = "Server=$InstanceName;Database=msdb;Integrated Security=True;"
#--Main Query --#
$query = "SELECT * FROM sysjobs"
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
$command = $connection.CreateCommand()
$command.CommandText = $query
$result = $command.ExecuteReader()
$table = new-object "System.Data.DataTable"
$table.Load($result)
#--Exporting data to the screen --#
$table | select $table.Columns.ColumnName | ConvertTo-Json
$connection.Close()
# Results
{
"job_id": "5126aca3-1003-481c-ab36-60b45a7ee757",
"originating_server_id": 0,
"name": "syspolicy_purge_history",
"enabled": 1,
"description": "No description available.",
"start_step_id": 1,
"category_id": 0,
"owner_sid": [
1
],
"notify_level_eventlog": 0,
"notify_level_email": 0,
"notify_level_netsend": 0,
"notify_level_page": 0,
"notify_email_operator_id": 0,
"notify_netsend_operator_id": 0,
"notify_page_operator_id": 0,
"delete_level": 0,
"date_created": "\/Date(1542859767703)\/",
"date_modified": "\/Date(1542859767870)\/",
"version_number": 5
}

The "rub" here is that the SQL command FOR JSON AUTO even with execute scalar, will truncate JSON output, and outputting to a variable with VARCHAR(max) will still truncate. Using SQL 2016 LocalDB bundled with Visual Studio if that matters.

Related

Powershell - mySQL Query, error every other run

I am working on a powershell script that needs some input from a mySQL database. For the life of me I can't tell what I've done wrong here.
Every other time I run this script, I get an error Exception calling "Open" with "0" argument(s): "Out of sync with server"[0]. So, the first run, it will pull the expected data and dump it on my screen, then on the next run I get that error. And the cycle just repeats. Here is my full code (right now its just a test query to pull then dump the data. If it matters, the mySQL server is running MariaDB 10.3.14 on a Ubuntu 18.04 host.
$error.Clear()
$sqlQuery = get-content -path "C:\querytext.sql" -Raw
$sqlUser = "myuser"
$sqlPass = "mypass"
$sqlHost = "myserver"
$sqlDB = "dbname"
$connectionString = "server= $sqlHost;port=3306;uid=$sqlUser;pwd=$sqlPass;database=$sqlDB"
Try{
$connection = New-Object MySql.data.MySqlClient.MySqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
$command = New-Object MySql.data.MySqlClient.MySqlCommand($sqlQuery,$connection)
$dataAdapter = New-Object MySql.data.MySqlClient.MySqlDataAdapter($command)
$dataSet = New-Object System.Data.DataSet
$dataAdapter.fill($dataSet, "data") | Out-Null
$command.Dispose()
$sqlResults = $dataSet.tables["data"]
}
Catch {
Write-Host "ERROR : Unable to run query : $query `n$Error[0]"
}
$connection.close()
$sqlResults | Format-Table
$sqlResults | ForEach-Object {
write-host $_.fname
}
Might I suggest using the SQL PS module:
https://learn.microsoft.com/en-us/sql/powershell/download-sql-server-ps-module?view=sql-server-2017
That page has installation instructions and its from Microsoft. Personally, the dotnet class you are using, it works, but its relatively difficult to work with.
Connecting to a DB is much simpler with this module and you do not have to worry about micromanaging connections.
Invoke-Sqlcmd -ServerInstance $sqlHost -Query $sqlQuery -Database $sqlDB -Username $sqlUser -Password $sqlPass
This will return a PS object like every other PS cmdlet.

How to add a SQL update statement to powershell after a foreach loop

I'm a noob with PowerShell. and have a question.
I have a working PS1 script that runs a SQL query to get a ID parameter, then creates a PDF from a Crystal Report, form another program based on that returned SQL query and then, loops through the returned parameters and repeats.
What I need is to add an SQL update statement to the PS1 script, that updates a table based on the same ID that is returned from the SQL query, after the PDF is created. Before moving on to the next ID, or after the script creates all the PDF's based on the Id's.
This is what I need to add to the script.
Update DB2.dbo.table2
SET DB2.dbo.table2.Field_01 = 1
FROM DB2.dbo.table2
WHERE (DB2.dbo.Table2.ID = {ID})
The SP1 script that works looks like this.
$serverName="MAIN"
$databaseName="DB1"
$sqlCommand="select ID from ID_Query"
$connectionString = "Data Source=$serverName; " +
"Integrated Security=SSPI; " +
"Initial Catalog=$databaseName"
$connection = new-object system.data.SqlClient.SQLConnection($connectionString)
$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
$connection.Open()
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
$connection.Close()
foreach ($Row in $dataSet.Tables[0].Rows)
{
$commandLine= -join(#'
"C:\Program Files (x86)\CR_Program\Program_name\CR_Printer.exe" -report="C:\Crystal_report.rpt" -exportformat=PDF -exportfile="c:\test\{Parameters.ID}.pdf" -parameters"
'#,$($Row[0]),#'
"
'#)
cmd /c $commandLine
}
I hoping to mark a column field_01 to 1, so the script does not create another Crystal repoort.pdf for the same ID, as the ID will be marked to 1 then the query that runs wont see it.
or maybe there a better way to do this.
Thanks.
You can add a timestamp to the filename along with the first field of your DataTable (query results) supposing it's an ID :
# After $connection.Close()
$crPrinter = "C:\Program Files (x86)\CR_Program\Program_name\CR_Printer.exe"
$report = "-report='C:\Crystal_report.rpt'"
foreach ($Row in $dataSet.Tables[0].Rows)
{
$now = [DateTime]::Now.ToString("yyMMddHHmmss")
$outputFile = "-exportfile='c:\test\Report_id_$Row[0]_$now.pdf' -parameters"
$commandLine= [string]::Format("{0} {1} {2}", $crPrinter, $report, $outputFile)
cmd /c $commandLine
Start-Sleep 1 # Sleeps the script to offset a second
}

Can not run Insert query against mySQL database using Powershell, but can via the PHPMyAdmin UI

Im trying to run the following query against my mySQL database :
"INSERT INTO present (name) VALUES ('fred');"
I can run this query in the UI in PHPMyAdmin and it created the expected data, however, I can not run it from my PowerShell script. I can get data from the database using my PowerShell script, but can't seem to be able to create any. Any ideas ?
EDIT
Code I use to connect to database :
function ConnectToDatabase([string]$user, [string]$pass, [string]$MySQLHost, [string]$database) {
Log "--------"
Log "Connecting to database"
# Load MySQL .NET Connector Objects
[void][system.reflection.Assembly]::LoadWithPartialName("MySql.Data")
# Open Connection
$connStr = "server=" + $MySQLHost + ";port=3306;uid=" + $user + ";pwd=" + $pass + ";database="+$database+";Pooling=FALSE"
try {
$conn = New-Object MySql.Data.MySqlClient.MySqlConnection($connStr)
$conn.Open()
} catch [System.Management.Automation.PSArgumentException] {
Log "Unable to connect to MySQL server, do you have the MySQL connector installed..?"
Log $_
#Exit
} catch {
Log "Unable to connect to MySQL server..."
Log $_.Exception.GetType().FullName
Log $_.Exception.Message
#exit
}
Log "Connected to MySQL database : $MySQLHost\$database"
Log "--------"
return $conn
}
Running the query
$conn = ConnectToDatabase $user $pass $MySQLHost $database
$query = "INSERT INTO present (name) VALUES ('fred');"
$Command = New-Object MySql.Data.MySqlClient.MySqlCommand $query, $conn
I also had this to GET data from the database but I thought it was redundant to the Insert of data
$dataAdapter = New-Object MySql.Data.MySqlClient.MySqlDataAdapter($Command)
$dataSet = New-Object System.Data.DataSet
$recordCount = $dataAdapter.Fill($dataSet, "data")
return $dataSet.Tables[0]
The code you posted creates a MySqlCommand object, but doesn't actually execute the SQL statement. Use the ExecuteNonQuery() method for that:
$conn = ConnectToDatabase $user $pass $MySQLHost $database
$query = "INSERT INTO present (name) VALUES ('fred');"
$Command = New-Object MySql.Data.MySqlClient.MySqlCommand $query, $conn
$Command.ExecuteNonQuery()

Is there a tool to load files into varbinary(max) fields?

I was contemplating the creation of a tool to load varbinary(max) fields in a SQL Server 2008 database with files as selected from a open file dialog. Is there any such tool or equivalent that I could use? My SQL server is in a hosted environment where I don't have physical access to the server so loading it with TSQL is not an option.
How about powershell?
# from: http://sev17.com/2010/05/t-sql-tuesday-006-blobs-filestream-and-powershell/
#
$server = "superfly\sqlexpress"
$database = "Yak"
$query = "INSERT dbo.FileStore VALUES (#FileData, #FileName)"
$filepath = "d:\yak.txt"
$FileName = get-childitem $filepath | select -ExpandProperty Name
$connection=new-object System.Data.SqlClient.SQLConnection
$connection.ConnectionString="Server={0};Database={1};Integrated Security=True" -f $server,$database
$command=new-object system.Data.SqlClient.SqlCommand($query,$connection)
$command.CommandTimeout=120
$connection.Open()
$fs = new-object System.IO.FileStream($filePath,[System.IO.FileMode]'Open',[System.IO.FileAccess]'Read')
$buffer = new-object byte[] -ArgumentList $fs.Length
$fs.Read($buffer, 0, $buffer.Length)
$fs.Close()
$command.Parameters.Add("#FileData", [System.Data.SqlDbType]"VarBinary", $buffer.Length)
$command.Parameters["#FileData"].Value = $buffer
$command.Parameters.Add("#FileName", [System.Data.SqlDbType]"NChar", 50)
$command.Parameters["#FileName"].Value = $FileName
$command.ExecuteNonQuery()
$connection.Close()

Run a SQL Script Against MySQL using Powershell

I have a Powershell script that backs up my MySQL DB's each night using mysqldump. This all works fine but I would like to extend the script to update a reporting db (db1) from the backup of the prod db (db2). I have written the following test script but it does not work. I have a feeling the problem is the reading of the sql file to the CommandText but I am not sure how to debug.
[system.reflection.assembly]::LoadWithPartialName("MySql.Data")
$mysql_server = "localhost"
$mysql_user = "root"
$mysql_password = "password"
write-host "Create coonection to db1"
# Connect to MySQL database 'db1'
$cn = New-Object -TypeName MySql.Data.MySqlClient.MySqlConnection
$cn.ConnectionString = "SERVER=$mysql_server;DATABASE=db1;UID=$mysql_user;PWD=$mysql_password"
$cn.Open()
write-host "Running backup script against db1"
# Run Update Script MySQL
$cm = New-Object -TypeName MySql.Data.MySqlClient.MySqlCommand
$sql = Get-Content C:\db2.sql
$cm.Connection = $cn
$cm.CommandText = $sql
$cm.ExecuteReader()
write-host "Closing Connection"
$cn.Close()
Any assistance would be appreciated. Thanks.
This line:
$sql = Get-Content C:\db2.sql
Returns an array of strings. When that gets assigned to something expecting a string then PowerShell will concatenate the array of strings into a single string using the contents of the $OFS (output field separator) variable. If this variable isn't set, the default separator is a single space. Try this instead and see if it works:
$sql = Get-Content C:\db2.sql
...
$OFS = "`r`n"
$cm.CommandText = "$sql"
Or if you're on PowerShell 2.0:
$sql = (Get-Content C:\db2.sql) -join "`r`n"