Oracle Sql and Powershell : Execute query, print/output results - sql

I need a way to execute a SQL (by importing a .SQL script) on a remote Oracle DB using PowerShell. In addition to this I am also trying to output the results in an .xls format in a desired folder location. To add to the fun, I would also want to run this task on an automatic schedule. Please help !
I have gotten so far :
[System.Reflection.Assembly]::LoadWithPartialName ("System.Data.OracleClient") | Out-Null
$connection = "my TNS entry"
$queryString = "my SQL query"
$command = new-Object System.Data.OracleClient.OracleCommand($queryString, $connection)
$connection.Open()
$reader = $command.ExecuteReader()
$tempArr = #()
#read all rows into a hash table
while ($reader.Read())
{
$row = #{}
for ($i = 0; $i -lt $reader.FieldCount; $i++)
{
$row[$reader.GetName($i)] = $reader.GetValue($i)
}
#convert hashtable into an array of PSObjects
$tempArr+= new-object psobject -property $row
}
$connection.Close()
write-host "Conn State--> " $connection.State
$tmpArr | Export-Csv "my File Path" -NoTypeInformation
$Error[0] | fl -Force

The easiest way is to drive sqlplus.exe via powershell. To execute the sql and get the output you do this:
$result = sqlplus.exe #file.sql [credentials/server]
#parse result into CSV here which can be loaded into excel
You can schedule this script with something like:
schtasks.exe /create /TN sqlplus /TR "Powershell -File script.ps1" /ST 10 ...
For this you need to have sqlplus installed (it comes with oracle express and you could install it without it). This obviously introduces dependency that is not needed but sqlplus could be used to examine the database and do any kind of thing which might be good thing to have around.

Related

How do you format XML output properly from Powershell using invoke-sqlcmd

Below is my PowerShell script that connects to a remote SQL server and stored the result into a XML file.
$SQLResult = Invoke-Sqlcmd -inputfile $inputfile -ServerInstance $ServerInstance -Database $Database -Username $Username -Password $Password
$PropertyName = ($SQLResult | Get-Member -MemberType Property | Where {$_.Name -like "XML*"}).Name
$SQLResult.$PropertyName | Out-File -FilePath "C:\Temp\ExportFile.xml" -Force
Ideally it should return sth clean and neat like this (Which is also the case when I open up the result in my SQL Server Management Studio):
<Text>
<Data>I am happy</Data>
</Text>
However, when I open the file, it gives me:
<Text><Data>I am happy</Data></Text>
I have tried to use Export-Clixml, but the XML returned is surrounded by some meaningless tags called <props> which is not one of my tags.
Can anyone help me out on this, not sure which way to go to save it in its original format.
Use an XmlWriter to format and indent it nicely when writing back to disk:
# Create settings object, make sure we get the indentation
$writerSettings = [System.Xml.XmlWriterSettings]::new()
$writerSettings.Indent = $true
try{
# Create the writer
$writer = [System.Xml.XmlWriter]::Create("C:\Temp\ExportFile.xml", $writerSettings)
# Convert your XML string to an XmlDocument,
# then save the document using the writer
([xml]$SQLResult.$PropertyName).Save($writer)
}
finally {
# discard writer (closes the file handle as well)
$writer.Dispose()
}

How doI use Powershell to take output from a SQL query and search another file for that output

This is my first time using Powershell so please forgive my ignorance.
I have a SQL query that returns back a bunch of order numbers. I want to check another file to see if there is an existing PDF in that file with the same name as the orders numbers returned by the SQL query.
Everything in my code works up until the ForEach loop which returns nothing. Based on my google searches I think I'm pretty close but I'm not sure what I'm doing wrong. Any help would be appreciated.
I've removed the actual file name for obvious reasons, and I do know that the file is correct and other commands do access it so I know that is not my problem at the moment. I've also removed sensitive info from the SQL query.
$statement = "SELECT A, Date FROM XXXX
WHERE STAT = 1 AND Date >= trunc(sysdate)"
$con = New-Object System.Data.OracleClient.OracleConnection($connection_string)
$con.Open()
$cmd = $con.CreateCommand()
$cmd.CommandText = $statement
$result = $cmd.ExecuteReader()
$list = while ($result.Read()) { $result["A"]}
Write-Output $list​
#########Loop through the list above here to check for matching PDF
ForEach ($Order in $list){
Get-ChildItem "\\xxxxxx\" -Filter $Order -File
#########If FALSE - notify that PDF is missing
}
$con.close()
I have also tried the following code, which gets me closer and actually finds the files I'm looking for, but gives the error
" Get-ChildItem : A positional parameter cannot be found that accepts argument
CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand"
ForEach ($Order in $list){
if((Get-ChildItem "\\xxxxx\" + $Order)){
Write-Output
} else { Write-Host "Does not exist."}
I gather from your comment that $list is an array of order numbers.
Next, you want to check if there is a file in a folder having that name, correct?
Then I'd suggest you use Test-Path instead of Get-ChildItem:
$folderToSearch = '\\servername\sharename\folder'
foreach ($Order in $list) {
$fileToCheck = Join-Path -Path $folderToSearch -ChildPath ('{0}.pdf' -f $Order)
if (Test-Path -Path $fileToCheck -PathType Leaf) {
"File found: $fileToCheck"
}
else {
"File $fileToCheck does not exist"
}
}

Invoke-AzVMRunCommand as a job

I am trying to use Invoke-AzVMRunCommand as a job. when I executed below script the job is created and executed successfully but I am failing to write the output like which job result belongs to which vm.
Invoke-AzVMRunCommand is used to invoke a command on a particular VM. You should have this information beforehand.
Here is some information on -AsJob parameter
https://learn.microsoft.com/en-us/powershell/module/az.compute/invoke-azvmruncommand?view=azps-2.6.0#parameters
As suggested by AmanGarg-MSFT, you should have that information before hand. You can use a hashtable $Jobs to store the server name and Invoke-AzVMRunCommand output and later iterate through using the $Jobs.GetEnumerator().
$Jobs = #{}
$Servers = "Server01","Server02"
[System.String]$ScriptBlock = {Get-Process}
$FileName = "RunScript.ps1"
Out-File -FilePath $FileName -InputObject $ScriptBlock -NoNewline
$Servers | ForEach-Object {
$vm = Get-AzVM -Name $_
$Jobs.Add($_,(Invoke-AzVMRunCommand -ResourceGroupName $vm.ResourceGroupName -Name $_ -CommandId 'RunPowerShellScript' -ScriptPath $FileName -AsJob))
}

Powershell XML encoding

I have a script that executes a stored procedure on a SQL Server which returns XML. I then have a function to to format the XML in powershell so it is readable. When i open the XML in Chrome i get this error:
This page contains the following errors:
error on line 149 at column 27: Encoding error
Below is a rendering of the page up to the first error.
I think I may need to encode it in UTF8 but I am unsure where to do it in my code. Any help to rectify the error or how to do the encoding is appreciated.
Here is the Powershell that I run to get the XML file:
function Format-XML {
[CmdletBinding()]
Param ([Parameter(ValueFromPipeline=$true,Mandatory=$true)][string]$xmlcontent)
$xmldoc = New-Object -TypeName System.Xml.XmlDocument
$xmldoc.LoadXml($xmlcontent)
$sw = New-Object System.IO.StringWriter
$writer = New-Object System.Xml.XmlTextwriter($sw)
$writer.Formatting = [System.XML.Formatting]::Indented
$xmldoc.WriteContentTo($writer)
$sw.ToString()
}
$Date = Get-Date -format "yyyyMMdd_HHmm"
$File = "C:\Temp\MyFile"+$Date+".xml"
$Query = "EXEC dbo.usp_MyProc"
$resultRow = Invoke-Sqlcmd -Query $Query -database MyDatabase -ServerInstance MyServer
Format-xml $resultRow['results'] | Set-Content -Path $File -Force
Comment "Try appending -Encoding UTF8 to your last line" from Martin Brandi worked

Powershell: How to Parse the Multi line String as a String parameter?

As a newbie in powershell, im trying to read thru a folder which has multiple sql files and iterate them through poweshell scripts read the data from oracle and export to CSV.
If my sqlfile has a single line statement no issues with the code, its working fine, If my sql file has multiple line statement - as always it has,
the powershell errors out saying
"Get-DataTable : Cannot process argument transformation on parameter 'sql' Cannot convert value to type System.String."
could you please help me how to resolve this issue? Below my code snapshot.
function Get-DataTable{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[Oracle.DataAccess.Client.OracleConnection]$conn,
[Parameter(Mandatory=$true)]
[string]$sql
)
$cmd = New-Object Oracle.DataAccess.Client.OracleCommand($sql,$conn)
$da = New-Object Oracle.DataAccess.Client.OracleDataAdapter($cmd)
$dt = New-Object System.Data.DataTable
[void]$da.Fill($dt)
return ,$dt
}
foreach ($file in Get-ChildItem -path $ScriptsDirectory -Filter *.sql | sort-object -desc )
{
$SQLquery = get-content "$ScriptsDirectory\$file"
echo $SQLquery
$fileName = $file.name.split(".")[0]
$dt = Get-DataTable $conn $SQLquery
Write-Host "Retrieved records:" $dt.Rows.Count -ForegroundColor Green
$dt | Export-Csv -NoTypeInformation -LiteralPath $WorkingDirectory\$fileName.csv
Write-Host "Output Written to :" $WorkingDirectory\$fileName.csv -ForegroundColor Green }
Get-Content returns an array of lines. If you're using PowerShell v3 or higher you can use the -Raw parameter to read the file as one big string:
$SQLquery = get-content "$ScriptsDirectory\$file" -Raw
Alternatively you could re-join the array with line endings:
$SQLquery = $SQLquery -join "`r`n"
Or you can read the file all at once with .net classes:
$SQLquery = [System.IO.File]::ReadAllText("$ScriptsDirectory\$file")