Sql query result as an array of objects powershell - sql

I'm trying to operate with the result of this query to then run an update query on specific values of the result. What i'm trying to do is to get all the values from the table and then check if those values are between 1 and 5 and turn those to null. Since i can't do this in one update query, i'm doing first a select and then operate on the singular values that i get from the result, but the query returns me a dataset result which i can't operate with in PowerShell (or at least i don't know how). What can i do? The main objective of this should be an update to all the columns of the table on the db to change the columns with values between 1 and 5 and turn them into null values
Here is the code:
$SQLServer = "Server\SQLEXPRESS"
$SQLDBName = "Prova"
$SqlQuery = "Select * from table_2 where id=1"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName; trusted_connection=true;"
$SqlConnection.Open()
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter.SelectCommand = $SqlCmd
$Dataset = New-Object System.Data.DataSet
$SqlAdapter.Fill($Dataset)
$array=$Dataset.Tables[0]
$SqlConnection.Close()

A fellow few-months old newbie here(me), ill try to give this a shot!
You can actually loop through the rows of the dataset you have, and access the properties (columns) in those rows, modify it and then dynamically create an update statement and execute it on your server.
The main part is presented below, the rest are just the functions i defined myself. Not sure if this is what you had in mind but my testing setup went something like this. (Note please execute/define the functions first in your powershell session before you run the code below)
# SET VARIABLES
$Serv = <Your Server>
$DB = <Your DB>
$TSQL = "SELECT * FROM TestTBL"
# Target Results table from SQL
$MainResultsTable = (GetSQLData $Serv $DB $TSQL).Tables[0]
#Get Column names
$Colnames = ($MainResultsTable.Rows | gm -MemberType NoteProperty,Property).Name
# Loop through each row of data from SQL results
foreach($row in $MainResultsTable.Rows)
{
# Construct the TSQL update statement. Using an array to construct the multi column updates.
$TSQLUpdate = "UPDATE TestTBL SET "
$TSQLUpdateArr =#()
foreach($Col in $Colnames)
{
# We don't need to update the ID
if($Col -ne 'ID')
{
$TSQLUpdateArr += "$Col = $(EvaluateColumnData $row.$Col)`n"
}
}
# join the columns with the corresponding end of TSQL where the target ID is specified
$TSQLUpdate += $($TSQLUpdateArr -join ",").ToString() + " WHERE ID = $($row.ID);"
# Execute the update on SQL server
UpdateSQL $Serv $DB $TSQLUpdate
}
Putting a few snippets of the functions I wrote for SQL here too. [Open to optimization and critics to make this faster or more 'semanticy']
# Define custom user function to set the values to be used for updating
function EvaluateColumnData()
{
param( $data )
if($data -le 5){ return "NULL" }
else { return $data }
}
# Get data from SQL
function GetSQLData()
{
param( $tgtServ,$tgtDB,$tgtTSQL )
# Create connection obj
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "server="+$tgtServ+";database="+$tgtDB+";trusted_connection=true;"
# Open SQL connection
$SqlConnection.open()
# Create TSQL CMD object and pass the connection object
$SQLCommand = New-Object System.Data.SQLClient.SQLCommand
$SQLCommand.Connection = $SqlConnection
# TSQL statement to be executed
$SQLCommand.CommandText = $tgtTSQL
$SQLCommand.CommandTimeOut = 0
# Container/adapter for SQL result
$resultAdapter = New-Object System.Data.SqlClient.SqlDataAdapter($SQLCommand)
# DataSet where the results are dumped
$resultDS = New-Object System.Data.DataSet
$resultAdapter.Fill($resultDS) | Out-Null
$SqlConnection.Close()
return ,$resultDS
}
# Execute TSQL statement without results
function UpdateSQL()
{
Param( $tgtServ,$tgtDB,$tgtTSQL )
$ServerConn = New-Object System.Data.SQLClient.SQLConnection
$ServerConn.ConnectionString = "server="+$tgtServ+";database="+$tgtDB+";trusted_connection=true;"
$ServerConn.Open()
$ServerCMD = New-Object System.Data.SQLClient.SQLCommand
$ServerCMD.Connection = $ServerConn
$ServerCMD.CommandText = $tgtTSQL
$ServerCMD.CommandTimeOut = 0
$ServerCMD.ExecuteNonQuery() | out-null
$ServerConn.Close()
}
Hope this helps. There are a lot of things out there you can read(which im still reading lol) which offers better explanation, I suggest focusing on the basics.
Recommended reading: DataTables, PS objects/Custom objects, hashtable, Functions.

Related

PowerShell function always returns 0 when SQL returns no record [duplicate]

This question already has an answer here:
Why does Range.BorderAround emit "True" to the console?
(1 answer)
Closed 25 days ago.
I am experiencing a very strange behaviour in PowerShell. Consider the following PowerShell function:
function Select-SQLRecords($connString, $query)
{
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = $connString
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $query
$SqlCmd.Connection = $SqlConnection
$dataSet = New-Object System.Data.DataSet
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$SqlAdapter.Fill($dataSet)
$SqlConnection.Close()
$data = $dataset.Tables[0].Rows
if($data.Count -eq 0)
{
return ""
}
return $data
}
When I call the function
$recordRows = Select-SQLRecords $connectionstring $sqlquery
if the query does result in rows being retrieved, then $recordRows will be set for those rows. However, if the query does not result in rows being retrieved, then $recordRows is set to 0, even when I specifically tell the function to return "".
I tried to test by placing return "" at different places in the function and found that the function will return whatever I want it to return if I place the return statement before $SqlAdapter.Fill($dataSet). But as soon as $SqlAdapter.Fill($dataSet) is executed, it will always return 0 if no rows retrieved.
Anyone experienced this before?
Cheers
Conax
After further research, I finally found the reason.
This line
$SqlAdapter.Fill($dataSet)
generates its own output (number of records filled into the dataset).
When this output is not captured, e.g.
$rowCount = $SqlAdapter.Fill($dataSet)
that output gets returned to the function caller as well.
Previously I didn't notice that when there were records returned, it also had a record count added to the function return.

PowerShell Query SQL IF Column = 0

I'm a bit out of my wheelhouse on this one, but what I'm trying to do is a have a web form gather all our new user information, dump it in a table then use those columns to populate the data for my PowerShell user provisioning scripts.
The part I'm struggling with is querying the data and getting it into variables in PowerShell. Assume a table with basic GivenName,SurName,Title etc. columns. I want to pull all rows of data (new users) that the "Created" column equals 0, and put each column into a variable to use. At the end of the user creation script, I will update the "Created" column to 1 to avoid the user being re-created next batch run but the row will remain.
Any tips on the best way to go about this?
So we do this alot, so much that we created a function we always leverage for taking data in or out of SQL.
function Get-SQL ($query,$server,$database, $username, $password) {
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
if ($PSBoundParameters.ContainsKey('username') -eq $true -and $PSBoundParameters.ContainsKey('password') -eq $true) {
$SqlConnection.ConnectionString = "Server = $server; Database = $database; User ID=$username; Password=$password;"
} else {
$SqlConnection.ConnectionString = "Server = $server; Database = $database; Integrated Security = True"
}
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $query
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet) | Out-Null
$SqlConnection.Close()
return $DataSet.Tables[0]
}
Now dont get scared of all that craziness, just put it at the top of your script (we actually have it in a global functional library we call on all of our automation scripts but putting it inline will be fine)
Once that function is there you can simply do this:
$MyData = get-sql -server "MyServer" -database "MyDatabaseName" -Query "select * from myTable where created = 0"
Then you will be able to access the all columns via the $MyData object. But in reality you will want to do a foreach loop to really work with each record.
foreach ($whatever in $myData) {
write-output "Now working on record: $($whatever.GivenName) $($whatever.SurName)"
# Do more stuff you want
#now Update SQL
get-sql -server "MyServer" -database "MyDatabaseName" -Query "update myTable set created = 1 where UniqueUserID = '$($whatever.UniqueUserID)'"
}
Last things to note, you need a unique User ID. This can be anything, here we always use objectGUID from Active Directory but if you dont have that then setup UserID field in the db, and have it auto increment an int. You always want a immutableID (meaning something that will never change like a name or an email will). Also notice when using vars in a string I always use $($var) syntax, works when your strings are in double quotes and can help avoid issues.
This should be enough to get you going... good luck!

Powershell script executing SQL procedure with parameters has truncated output

I have a Powershell script that is successfully connecting to a SQL Server database and executing a procedure. The SQL procedure contains a select as follows:
SELECT #sql AS 'ColDemo1'
The variable #sql is nvarchar(max). I want the full contents of #sql returned to a new sql file however I only get the first line and an unwanted column heading as below:
ColDemo1
------------
First line of data...
The Powershell script is as follows:
$server = "SERVER\DEMO"
$database = "dbDemo"
$pZero = "PVal0"
$pOne = "PVal1"
$pTwo = "PVal2"
$pThree = "PVal3"
function Run-SQLUSP {
param (
$pZero,
$pOne,
$pTwo,
$pThree
)
$conn = New-Object System.Data.SqlClient.SqlConnection("Server=${server};Database='${database}';Integrated Security=TRUE")
$conn.Open()
$cmd = $conn.CreateCommand()
$cmd.CommandText = "dbo.demoSp '$pZero', '$pOne', '$pTwo', '$pThree'"
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter($cmd)
$dataset = New-Object System.Data.DataSet
[void]$adapter.Fill($dataset)
$dataset.tables[0]
}
Run-SQLUSP $pZero $pOne $pTwo $pThree | Out-File "c:\DemoFolder\DemoScriptName.sql" -width 8000
How can I amend my Powershell script to return all lines of the output to the new sql file? When executing the procedure in SSMS the resultset is returned in a single table cell (not multiple rows).
Two changes were required:
To avoid truncating the output: Change Out-File to Set-Content -Path and remove -width 8000.
To only return the first cell: Append .rows[0][0] to $dataset.tables[0] within the function.
Below is the revised code in full:
$server = "SERVER\DEMO"
$database = "dbDemo"
$pZero = "PVal0"
$pOne = "PVal1"
$pTwo = "PVal2"
$pThree = "PVal3"
function Run-SQLUSP {
param (
$pZero,
$pOne,
$pTwo,
$pThree
)
$conn = New-Object System.Data.SqlClient.SqlConnection("Server=${server};Database='${database}';Integrated Security=TRUE")
$conn.Open()
$cmd = $conn.CreateCommand()
$cmd.CommandText = "dbo.demoSp '$pZero', '$pOne', '$pTwo', '$pThree'"
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter($cmd)
$dataset = New-Object System.Data.DataSet
[void]$adapter.Fill($dataset)
$dataset.tables[0].rows[0][0]
}
Run-SQLUSP $pZero $pOne $pTwo $pThree | Set-Content -Path "c:\DemoFolder\DemoScriptName.sql"
I was having the same issue, then discovered I could fix it if I used the Column Name to access the cell value from the table row.
#Trunacted
$data.Tables[0].Rows[0][0]
#Complete data
$data.Tables[0].Rows[0]["XMLData"]

SQL Query with Powershell - Return Values not maching

So I have some code I wrote to take the input form a text file and run some sql checks against another database I populated:
$volOutput = gc C:\Users\<user>\Desktop\mutant.txt
foreach ($m in $volOutput) {
$check = $m.split()[-1] | select -Unique
foreach ($c in $check) {
#$c - this lists all of them so the foreach is working...
# Build the connection and search the db for $c names.
$conn = New-Object System.Data.SqlClient.SqlConnection
$conn.ConnectionString = "Server=(localdb)\mutex; Database=MutexObjects"
$conn.Open()
$db = $conn.CreateCommand()
$db.CommandText = "select Names from Objects WHERE Names='$c'"
$db.ExecuteScalar()
$conn.Close()
} # Foreach Check
} # First foreach
The return values I get are:
PS C:\> B:\Programming\powershell\parse_vol.ps1
ZonesCounterMutex
ZoneAttributeCacheCounterMutex
ZonesCacheCounterMutex
ZoneAttributeCacheCounterMutex
ZonesLockedCacheCounterMutex
ZonesCounterMutex
ZoneAttributeCacheCounterMutex
ZonesCacheCounterMutex
ZoneAttributeCacheCounterMutex
ZonesLockedCacheCounterMutex
Which is correct, but it's also missing a lot more. If I take individual samples and run queries from within SQL management studio for example, I get:
I populated the word "test" in each list as a....test.
Select Names From Objects WHERE Names='test'
Names
test
But I don't see test on the output from the above code. There are about 5 or 6 more than it's missing that I have validated manually by querying the db w/in SQL management studio.
Any help is much appreciated.
Compare the file content against the full list of Names from the database:
$filecontent = Get-Content "C:\Users\<user>\Desktop\mutant.txt" `
| % { $_.split()[-1] } `
| select -Unique
$conn = New-Object System.Data.SqlClient.SqlConnection
$conn.ConnectionString = "Server=(localdb)\mutex; Database=MutexObjects"
$conn.Open()
$dbwrite = $conn.CreateCommand()
$dbwrite.CommandText = "SELECT Names FROM Objects"
$reader = $dbwrite.ExecuteReader([System.Data.CommandBehavior]::CloseConnection)
$dbcontent = while ( $reader.Read() ) { $reader[0] }
$conn.Close()
Compare-Object $filecontent $dbcontent
Does Compare-Object show differences?

How to display xml content from sql table by powershell?

What I need to do are:
1, query a row of xml from a sql server datatable. See pic below,the Row named StageDesccontents xml file.
2, the xml file contents a path //sharespace/test1/10.0.1212.0which I need to get, this was forming as<releasepath>//sharespace/test1/10.0.1212.0</releasepath> in the xml file.
Here are my codes try to get it:
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlQuery = "SELECT Stage.Description as StageDesc,Stage.StageStatusId FROM [Build].[dbo].[WorkflowInstance_View] as Build
join [Build].[dbo].[Stage_View] as Stage on Build.Id=Stage.[WorkflowInstanceId] where Stage.ParentId is null and Stage.StageStatusId <>4 and Stage.StageStatusId <>7 order by Build.Id desc"
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $Connection
$DBResult = $sqlcmd.ExecuteReader()
$DataTable = New-Object system.data.datatable
$DataTable.load($DBResult)
foreach ($StageDesc in $DataTable) {
[XML]$ReturnedXML=$StageDesc.releasepath
}
The code passed but returned nothing. Why this happened? Could anybody would like to help me?
You're assigning your xml data to a variable $RetrunedXML and overwriting the assignment on each iteration of your foreach. Have you checked $ReturnedXML?
Using the sample database for SQL Server 2008, I can use this:
$serverName = "$env:computername\sql1"
$databaseName = "AdventureWorks"
$query = "SELECT * from Person.Contact where AdditionalContactInfo IS NOT NULL"
$conn=new-object System.Data.SqlClient.SQLConnection
$connString = “Server=$serverName;Database=$databaseName;Integrated Security=SSPI;”
$conn.ConnectionString=$connString
$conn.Open()
$cmd=new-object system.Data.SqlClient.SqlCommand($Query,$conn)
$da = New-Object “System.Data.SqlClient.SqlDataAdapter” ($cmd)
$dt = New-Object “System.Data.DataTable”
$da.fill($dt) | out-null
$conn.Close()
$dt | foreach {[xml]$ReturnedXML = $_.AdditionalContactInfo; $ReturnedXML}
All you do in the code is declaring and assigning variables. There is no code that outputs or displays anything. Nor do you return any variable. So what do you expect the code should return? In which line? Did you even try to debug the code?
$da.fill($dt)
Loads the query results into DataTable $dt.
$dt | Out-GridView
Shows all the data.
The script worked great for me (except the last line, which didn't apply for my case).