Unable to Get Information using PowerShell to Query Oracle - database

I use PowerShell to query SQL databases, and I am quite familiar with that process. However, I am now tasked with building an automated task that queries Oracle for information.
It seems straight forward: Install proper Oracle DLL's, import them into PS, execute the query much like SQL. However, this is not the case. All I get when I request information is a list called FieldCount. This seems to imply that I am able to see the information, it's just not displaying correctly. I'd like the actual values, and nothing seems to get this for me.
Thanks to anyone who knows anything about this, as my hands are tied and this is the only way I can think of to get this information from Oracle on a scheduled basis. I am not the Oracle admin, I only have read access to this view.
function Get-OracleData($cmdText){
Add-Type -Path 'C:\app\client\username\product\12.1.0\client_1\odp.net\managed\common\Oracle.ManagedDataAccess.dll'
$username = 'username'
$password = 'password'
$con = New-Object Oracle.ManagedDataAccess.Client.OracleConnection('User Id=$username;Password=$password;Data Source=OracleServerName')
$con.Open()
$cmd = New-Object Oracle.ManagedDataAccess.Client.OracleCommand
$cmd.Connection = $con
$cmd.CommandText = $cmdText
$rdr = $cmd.ExecuteReader()
if($rdr.Read()){
return $rdr
}else{return 0}
}
Get-OracleData -cmdText '
SELECT em.employee_number,
em.last_name,
em.first_name,
em.middle_names,
em.email_address,
em.start_date,
em.term_date,
em.location_addr_line_1,
em.location_city,
em.location_work_state,
FROM CustomView em
'

Found the answer in the link below. I was able to get what I needed by inserting the below code at the line where $cmd.CommandText = $cmdText is located in my original post, and getting rid of what's below it.
$ds = New-Object system.Data.DataSet
$da = New-Object Oracle.ManagedDataAccess.Client.OracleDataAdapter($cmd)
[void]$da.fill($ds)
return $ds.Tables[0] | Select *
This returns to a variable, and I can get the first entry using $results[0], and $results[0].EMPLOYEE_NUMBER, etc.
Reference: http://poshcode.org/3965 #line55

Related

How to store a SQL Server query result to Powershell Array?

I am new to PowerShell scripting and currently working on a script to load the result of SQL Server query to store as a PowerShell array. Below is my code for reference. :
$SQLServer = 'MyServer';
$Database = 'Test';
## - Connect to SQL Server using non-SMO class 'System.Data':
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection;
$SqlConnection.ConnectionString = `
"Server = $SQLServer; Database = $Database; Integrated Security = True";
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand;
$SqlCmd.CommandText = $("select distinct Servername from dbo.tableA
where Servername like '%hw%'");
$SqlCmd.Connection = $SqlConnection;
$SqlCmd.CommandTimeout = 0;
## - Extract and build the SQL data object '$DataSetTable':
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter;
$SqlAdapter.SelectCommand = $SqlCmd;
$DataSet = New-Object System.Data.DataSet;
$SqlAdapter.Fill($DataSet);
$SqlConnection.Close()
$Servername = #[SqlAdapter]
I expect $Servername to be an array having data elements store as "Server1', 'Server2', 'Server3', etc. based on the sqlquery result. I am planning to utilize $Servername array to loop through each server in future. For now, I am able to successfully connect to database, but I am still not able to get the query result to store in a PowerShell array. Can someone please guide on where I am making mistake?
$SqlAdapter.Fill($DataSet)
This fills the [System.Data.DataSet] instance stored in $DataSet with the query results, which is why you must use $DataSet to get the data you need (untested):
$serverNames = $DataSet.Tables[0].Server
.Tables[0] accesses the first and only [System.Data.DataTable] instance in the dataset containing the query results.
.Server retrieves the the values of the query result's Server column, courtesy of PowerShell's member-access enumeration.
Note that this means that if there's only one result row, $serverNames will contain a single string rather than a single-element array containing that string.
To ensure that an array is always returned, use $serverNames = #($DataSet.Tables[0].Server), or, with a (strong) type constraint, [string[]] $serverNames = $DataSet.Tables[0].Server
As for what you tried:
PowerShell statements only ever need to be separated with ; if they're placed on the same line, which means that all the ; instances in your code are unnecessary.
While $("select distinct Servername from dbo.tableA where Servername like '%hw%'") technically works, there is no reason to wrap a double-quoted string literal ("...") in the subexpression operator - just omit the $(...) enclosure.
As for $Servername = #[SqlAdapter]: perhaps that was just pseudo code, but, to be clear: #[...] isn't a valid syntax construct in PowerShell (at least as of PowerShell 7.3[1]).
[1] By curious coincidence, #[...] just came up as potential future syntax for simplifying PowerShell's [pscustomobject] object-literal syntax - see GitHub issue #18747.

How to get the compatibility level of a database from analysis server?

I searched around and found there is a way to get compatibility level of a database in powershell through something like:
Import-Module SqlServer
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server=$Server;Initial Catalog=$DB;Integrated Security=SSPI"
$conn = new-object Microsoft.SqlServer.Management.Common.ServerConnection($sqlConnection)
$srv = new-object Microsoft.SqlServer.Management.Smo.Server($conn)
$db = New-Object Microsoft.SqlServer.Management.Smo.Database
$db = $srv.Databases.Item("$DB")
$comp_lvl = New-Object Microsoft.SqlServer.Management.Smo.CompatibilityLevel
$comp_lvl = $db.CompatiblityLevel
Write-Host "Compatibility level =" $db.CompatibilityLevel
However, I am getting errors trying to get compatibility level of a database that is on an analysis server
Exception getting "Item": "Failed to connect to server ..."
I realized it is probably working for a regular database engine, but something else may be used for an analysis server. I looked around on MS Docs and didnt really find anything helpful.
SO is this even possible?
UPDATE:
I was able to find something on this page: https://technet.microsoft.com/en-us/hh213141(v=sql.100)
Import-Module SqlServer
$as = New-Object Microsoft.AnalysisServices.Server
$as.connect("$Server")
$as.databases
Write-Host "Compatibility level ="$as.DefaultCompatibilityLevel
but this returns ALL databases back...
I want to specify just one database to get the compatibility level of...
I tried this,
$as.databases["$Database"]
but it seems not to return the proper level, because the DB i am passing has a lvl of 1103, not 1200...
I figured it out!
Import-Module SqlServer
$as = New-Object Microsoft.AnalysisServices.Server
$as.connect("$Server")
$c = $as.Databases | Where-Object { $_.ID -eq $Database }
Write-Host "Compatibility level =" $c.CompatibilityLevel

How to use PowerShell to batch call Update-Database

We use an Azure Elastic Pool resulting in multiple client databases and one master database with references to the client database.
We already have multiple databases and are working on a new version of the code. We use EF6 Code-First.
When we make a change to our model (add a property) we create the migration file and need to call Update-Database for all existing client databases.
This is monkey work we want to skip.
I already have a Powershell script to connect to the master database and execute a query on a table. This returns the names of the child databases.
With it I can change the Web.config and replace the Template database name with the proper name of the child database.
Now I need to call Update-Database to execute the migration scripts. With this last part I'm struggling because I'm running the ps1-script outside Visual Studio and thus the command Update-database is unknown. I tried using migrate.exe but then I get lots of errors.
I think the easiest solution is to run my script within the Package manager console but I can't figure out how to do that.
I managed to get it working. After I placed the ps1-file in the root of my code folder I could run it in the Package Manager Console using .\UpdateDatabases.ps1.
For completeness here's the script I created. I'm new to PowerShell so some optimizations might be possible.
cls
$currentPath = (Get-Item -Path ".\" -Verbose).FullName
#Read Web.config
$webConfig = $currentPath + "\<your project>\Web.config"
$doc = (Get-Content $webConfig) -as [Xml]
$DatabaseNamePrefix = $doc.configuration.appSettings.add | where {$_.Key -eq 'DatabaseNamePrefix'}
#Get Master connectionstring
$root = $doc.get_DocumentElement();
foreach($connString in $root.connectionStrings.add | where {$_.Name -eq "Master"})
{
$masterConn = $connString.connectionString
}
#Connect to master database
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = $masterConn
#Query Client table for the child database names
$SqlQuery = "select Code from Clients"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
#Put query result in dataset
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
foreach ($row in $DataSet.Tables[0].Rows)
{
$clientDbName = $row[0].ToString().Trim()
#Change Web.Config
foreach($connString in $root.connectionStrings.add | where {$_.Name -eq "DevelopmentDb"})
{
$newDatabaseName = "Database=" + $DatabaseNamePrefix.value + $clientDbName + ";";
$newConn = $connString.connectionString -replace "(Database=.*?;)",$newDatabaseName
$connString.connectionString = $newConn;
}
$doc.Save($webConfig)
#Update database
Update-Database -ConfigurationTypeName Application
}
"Finished"
You may want to take a look at Azure Elastic Database Jobs. Which is designed to work with the elastic database pools.
The Elastic Database Jobs SDK includes also PowerShell components.

Pulling info from SQL Server with Powershell

I am tasked with pulling information from SAP and cross referencing it to information pulled from Active Directory. The first step I need to obviously accomplish would be to figure out how to pull certain information out of the SQL Server. Here is what I have so far (thanks to several users here who have answered somewhat similar questions):
$SQLServer = "WEB-PRDSQ"
$SQLDBName = "PRD"
$SqlQuery = "SELECT * from prd.ZEMPLOYEE;"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName; User ID = DELETED; Password = DELETED;"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$DataSet.Tables[0] | out-file "C:\Scripts\SQL\SQLData.csv"
And this is the output it gives me: http://i.imgur.com/R6Y5HU2.png
For one, this is an ugly layout that is hard to read when there are 145,361 lines of text. And 2, I don't need all this information. All I need is their sAMAccountName (I don't see this as one of the output, but that's what it's called in AD), Emp_ID, Status, Hire_Date, Location, Emp_Title, LastLogonDate (once again, don't see this as an output but I know it's in AD), and lastly Term_Date.
When I try to change the "SELECT * from prd.ZEMPLOYEE" and change out the * for Status, Hire_Date, etc, it gives the error "Invalid column name 'Status'" (or whatever column I have listed first).
Is there someone out there who is patient enough to help work me through this and help me create this? I've only taken one Database class so I kinda know what I am talking about, but also don't know the intricate details that this may require. I am willing to help provide any information I need to.
Per earlier conversation...
Database Collation is case sensitive and column names must match the same case shown in the results file from the Select * From... query.
Example:
Select EMP_ID, STATUS From prd.ZEMPLOYEE

Powershell: Retrieve xml data from column in SQL Server database

Following this tutorial I tried to use PowerShell to retrieve xml data from SQL Server, but I only get one element back.
Here is a query to show the actual data:
But running this script I only get one element back:
$SQLServer = 'MYSERVER,1433'
$SQLDBName = "test"
$Query =
#'
USE test
SELECT EventLogXML FROM ForwardedEvents
'#
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $Database; Integrated Security = True"
$SqlConnection.open()
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $Query
$SqlCmd.Connection = $SqlConnection
$xr = $SqlCmd.ExecuteXmlReader()
$xd = New-Object System.Xml.XmlDataDocument
$xd.Load($xr)
$xr.Close()
$SQLConnection.Close()
$xd
$xd only has one element. What am I doing wrong?
---edit
I can confirm its only one xml doc by doing $xd.outerxml which reveals the complete doc. It is only one of the thousand or so event xml docs I'm storing in the EventLogXML column.
I think that XmlDataDocument is mainly for returning a single xml. Basically if you do in sql select * from bla for xml, auto you then can read it with the ExecuteXmlReader and XmlDataDocument. This is not what you want.
Modifying the example you linked to your needs we'll get somethign like:
$con = New-Object System.Data.SqlClient.SqlConnection
$con.ConnectionString = "Server=.; Database=AdventureWorks2012;Integrated Security=true"
$con.open()
$cmd = New-Object System.Data.SqlClient.SqlCommand
$cmd.CommandText = "SELECT Instructions FROM Production.ProductModel WHERE Instructions is not null"
$cmd.Connection = $con
$as = New-Object System.Data.SqlClient.SqlDataAdapter
$ds = New-Object System.Data.DataSet
$as.SelectCommand = $cmd
$as.Fill($ds);
$xmlDocs = $ds.Tables[0] | %{ [xml]$_.Instructions }
Now xmlDocs will contain a list of xml documents, one document per row.
Powershell wraps XML stuff into handy little objects, which you can explore using .Property syntax. If you just look at $xd, powershell by default will only show you the root node.
I don't know the structure of your XML column, but if the root node is called MyRoot, followed by common subnodes called MySub, try something like this:
$xd.MyRoot.MySub
This is just as the linked example shows the need to use $xd.root.Location
Edit
Ok so that is not the problem. Looks like it is by-design to return back only the first row when calling ExecuteXmlReader with a normal select statement (doc here):
if more than one row is returned, the ExecuteXmlReader method attaches
the XmlReader to the value on the first row, and discards the rest of
the result set
From some basic searching around, this blog post seems to explain the issue the best, and provides a workaround. See also here.
I may be out to lunch, but couldn't it be because you are declaring the database as $SQLDBName and then trying to connect to $Database in your connectionstring?

Resources