Pass a query and access SQL Server Database in Powershell - sql-server

I have a database named 'SQLDatabase'. It has many folders like 'System _Database', 'R_Database','ReportServer' etc.
I need to access 'R_Database'.
Now 'R_Database' has many folders like 'Storage', 'Security', 'Usage', 'Tables'.
I need to access 'Tables'.
Again, 'Tables' have many tables in it.
I need to access a particular table named 'DB_Batch', and update it.
How should I?
This is the code I did for connecting to the SQL Server, and then the Table. But it fails.
$dataSource = ".\MSSQL"
$user = "userID"
$pwd = "password01"
$database = "SQLDatabase"
$connectionString = "Server=$dataSource;uid=$user; pwd=$pwd;Database=$database;Integrated Security=False;"
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
#$connection.ConnectionString = "Server=$dataSource;Database=$database;Integrated Security=True;"
$connection.Open()
query = <query>..????
Now I suppose I need to pass a query. How should I?
I need to access the table first and then update it.
I even wrote the following code to end the above code, but not able to test as I am stuck.
$command = $connection.CreateCommand()
$command.CommandText = $query
$result = $command.ExecuteReader()
$result
I request, please help me with this. I have been trying this for days. I am using Powershell v2.0

In order to insert/update a table, use a SqlCommand and its ExecuteNonQuery().
$cmd = New-Object System.Data.SqlClient.SqlCommand
$cmd.connection = $connection
$cmd.commandtext = "INSERT INTO myTable (myColumn) values ({0})" -f $myValue
$cmd.ExecuteNonQuery()

Related

MSSQl 'GO' handling in Powershell connection String

I have a sql query which is patched with a Go to check the orphan objects in mssql.
use DBName
go
sp_change_users_login 'report'
Now, I am automating the above in Powershell for all the user databases and trying to get the orphan users.
Here is the code:
if($port)
{
$connectionString ="server=$servername,$port;Integrated Security=true;" #uid=$DBUserName; pwd=$dbpwd;Database=$DB;
}
else
{
$connectionString ="server=$servername;Integrated Security=true;"
}
$connection = New-Object System.Data.SqlClient.SqlConnection -ea Stop
$connection.ConnectionString=$connectionString
$connection.Open()
$db_query = #"
Select name from sys.sysdatabases where dbid > 4 and name not in ('ReportServer')
"#
$command = $connection.CreateCommand()
$command.CommandText = $db_query
$result = $command.ExecuteReader()
$object= New-Object System.Data.DataTable
$object.Load($result)
[System.Array]$DBs = $object.name
if($DBs -is [System.Array])
{
foreach($DB in $DBs)
{
## PROBLEM IS HERE ###
$orphan_users_query = #"
use $DB
GO
sp_change_users_login 'report'
"#
$command = $connection.CreateCommand()
$command.CommandText = $orphan_users_query
$result = $command.ExecuteReader()
$object= New-Object System.Data.DataTable
$object.Load($result)
$object | Out-File C:\temp\outfile_property.txt -Append -Force
}
}
Problem is PS cannot identify the go separator because it is specific to MSSQL/SSMS. So, how can I still iterate and run the query in all user databases without creating multiple connections specific to each DB?
Calling ChangeDatabase on the connection is an alternate means of switching databases.
You can then just execute sp_change_users_login as is. No need for GO.

ADO.NET query not showing in XEvents

I sent a query to SQL Server using an ADO.Net SqlAdapter from PowerShell. The query returns the correct result.
When I run an XEvent session with the sqlserver.sql_statement_starting event, the query from ADO.NET does not show up. Queries I sent from SSMS are shown immediately.
Is this a bug, or why do I not see the ADO.NET queries?
The code I am using is
$serverName = 'localhost'
$databaseName = 'Contoso Retail DW'
$schemaName = 'dbo'
$tableName = 'FactSalesFMCG'
$connString = Get-ConnectionString -IntegratedSecurity -Server $serverName -Database $databaseName
$sqlConn = [System.Data.SqlClient.SqlConnection]::new($connString)
$sqlConn.Open()
$columnMetadataAdapter = Get-ColumnMetadataAdapter -Conn $sqlConn -SchemaName $schemaName -TableName $tableName
$table = [System.Data.DataTable]::new()
$columnMetadataAdapter.Fill($table)
$sqlConn.Close()
Get-ConnectionString and Get-ColumnMetadataAdapter are PowerShell functions that assist in creating the needed ADO.NET objects. The table gets filled with the column metadata I wanted, but the SELECT statement is not shown in XEvents.
Can't repro.
CREATE EVENT SESSION [trc] ON SERVER
ADD EVENT sqlserver.rpc_completed,
ADD EVENT sqlserver.sp_statement_completed,
ADD EVENT sqlserver.sql_batch_completed,
ADD EVENT sqlserver.sql_statement_completed
GO
Start the session and watch live events in SSMS.
Then
PS C:\Users\dbrowne> $da = new-object system.data.sqlclient.sqldataadapter
PS C:\Users\dbrowne> $con = new-object system.data.sqlclient.sqlconnection "server=.;database=tempdb;integrated security=true"
PS C:\Users\dbrowne> $con.open()
PS C:\Users\dbrowne> $cmd = $con.createcommand()
PS C:\Users\dbrowne> $cmd.commandtext = "select * from sys.objects"
PS C:\Users\dbrowne> $da.selectcommand = $cmd
PS C:\Users\dbrowne> $dt = new-object system.data.datatable
PS C:\Users\dbrowne> $da.fill($dt)
106
and see both the sql_statement_completed and the sql_batch_completed (note with different code you might get an rpc_completed instead of a sql_batch_completed).
If you bind parameters into the SqlCommand it will be sent as an RPC call instead of a batch call, and the events will be a bit different rpc/sp instead of batch/sql.
rpc_completed/sp_statement_completed
instead of
sql_batch_completed/sql_statement_completed

How do I execute a SELECT query against a SQLServer database and iterate results using PowerShell

Say I have a table with 3 columns - "Column1", "Column2", and "Column3" - datatype is varchar(100) for all 3.
Using PowerShell, how do I connect to SQL Server and use SqlDataReader and ForEach operator to view the contents of "Column2"?
Here's roughly how I'm doing it:
$SqlServer = 'sql.example.com';
$SqlDatabase = 'MyDB';
$SqlConnectionString = 'Data Source={0};Initial Catalog={1};Integrated Security=SSPI' -f $SqlServer, $SqlDatabase;
$SqlQuery = "SELECT Name FROM dbo.Person ORDER BY Name;";
$SqlConnection = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $SqlConnectionString;
$SqlCommand = $SqlConnection.CreateCommand();
$SqlCommand.CommandText = $SqlQuery;
$SqlConnection.Open();
$SqlDataReader = $SqlCommand.ExecuteReader();
#Fetch data and write out to files
while ($SqlDataReader.Read()) {
Write-Output $SqlDataReader['Name'];
}
$SqlConnection.Close();
$SqlConnection.Dispose();
If I remember right, I basically refactored the code from the MSDN example.
For those wondering why I'm using SqlDataReader: Most of my scripts use SqlDataAdapter, but this one retrieves about 8,000 PDFs from a database so I wasn't really interested in calling SqlDataAdapter.Fill(). In exchange for holding shared locks on the table much longer than SqlDataAdapter.Fill() would, SqlDataReader.Read() keeps memory usage down to a manageable level for the client by fetching one record at a time.

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