How to isert or update an database using powershell - powershell

I need some help with a piece of script.
The situation is as followed:
I have an database in oracle and i would like to insert bios data using powershell. The problem is that i cant update the database with an if else statment because he keeps saying the if is true.
$bios = Get-WmiObject -Namespace root/hp/instrumentedBIOS -Class hp_biosSetting
($bios | Where-Object {$_.Name -eq 'boot order'})
$bios4 =echo $bios3.PSComputerName
$id ="select id FROM TESTAAD"
$CUR="SELECT id FROM TESTAAD WHERE WAARDE1 = $bios4"
if ($CUR -ne $id){
$qu= "INSERT INTO TESTAAD VALUES('$bios4','$waarde6','$waarde5','$waarde4','$waarde3','$waarde2','$waarde1','$id')"
$cmd = $con.CreateCommand()
$cmd.CommandText = $qu
$cmd.ExecuteNonQuery() |out-null
}
else
{
echo $bios4
$qu= "UPDATE TESTAAD SET waarde2 = '$waarde2' , waarde3 = '$waarde3' , waarde4 = '$waarde5' , waarde5 = 'bla' , waarde6 = '$waarde6' WHERE WAARDE1 = '$bios4'"
$cmd = $con.CreateCommand()
$cmd.CommandText = $qu
$cmd.ExecuteNonQuery() |out-null
echo $bios4
}
$con.CLose()
What I would like to acomplise today is to first insert data with an id and pc name en then update the data

The problem lies within non-updated variable. The $CUR is being assigned once, but never updated.
You are comparing if strings select id FROM TESTAAD and SELECT id FROM TESTAAD WHERE WAARDE1 = $bios4 are not equal. I'd assume you'd want to compare if the SQL select statement results are the same.
It seems like you are missing the actual execution for the SQL statements that $id and $CUR contain. The SQL queries sure don't do that:
$id ="select id FROM TESTAAD" # This is not performing an actual SQL query, only text
$CUR="SELECT id FROM TESTAAD WHERE WAARDE1 = $bios4" # Ditto
if ($CUR -ne $id){ # This is always true, as $id is not equal to $CUR
$qu= "INSERT INTO TESTAAD VALUES ('$bios4','$waarde6','$waarde5', `
'$waarde4','$waarde3','$waarde2','$waarde1','$id')"
$cmd = $con.CreateCommand()
$cmd.CommandText = $qu
$cmd.ExecuteNonQuery() |out-null
} else {
echo $bios4
$qu= "UPDATE TESTAAD SET waarde2 = '$waarde2' , waarde3 = '$waarde3', `
waarde4 = '$waarde5' , waarde5 = 'bla' , waarde6 = '$waarde6' WHERE WAARDE1 = '$bios4'"
$cmd = $con.CreateCommand()
$cmd.CommandText = $qu
$cmd.ExecuteNonQuery() |out-null
echo $bios4
}

Related

PowerShell Array to Table like output -- Possible ForEach problem

I'm trying to run a sql query via PowerShell and return the results in a table-like format.
It's putting multiple results in one field. I suspect there's something wrong with the 'foreach' loops. What am I missing, please?
To use the code below, just change the server names from 'server1'/'server2' for your sql server instances.
$query = "
SELECT ##SERVERNAME AS ServerName
, (SELECT DB_NAME ()) AS DBName
, s.name AS SchemaName
, st.name AS TableName
, RIGHT(st.name, 8) AS Rgt8
, TRY_CONVERT(DATE, RIGHT(st.name, 8), 103) AS Rgt8Date
FROM sys.tables AS st
INNER JOIN sys.objects AS so ON so.object_id = st.object_id
INNER JOIN sys.schemas AS s ON s.schema_id = st.schema_id
"
$instanceNameList = #('server1', 'server2')
$report = #()
foreach ($instanceName in $instanceNameList) {
write-host "Executing query against SERVER/INSTANCE: " $instanceName
$dbNames = Invoke-DbaQuery -SqlInstance $InstanceName -Database "master" -Query "select name from sys.databases where database_id > 4 and name <> 'TEST'"
foreach ($database in $dbNames.Name ) {
Write-host -Activity "Current DATABASE $database" -Status "Querying: $database"
$results = Invoke-DbaQuery -SqlInstance $InstanceName -Database $database -Query $query
# <#
if ($results -is [array]) {
$CustomObject = [pscustomobject] #{
ServerName = $results.ServerName
DBName = $results.DBName
SchemaName = $results.SchemaName
TableName = $results.TableName
Rgt8 = $results.Rgt8
Rgt8Date = $results.Rgt8Date
OverOneYearOld = $results.OverOneYearOld
Drop_Table = $results.Drop_Table
}
## ADDING EACH ROW/JOB OBJECT THAT HAS BEEN REPORTED, TO THE REPORT ARRAY
$report += $CustomObject
}
}
}
$report | Select-Object ServerName, DbName, TableName | Out-GridView
Basically, you're doing the opposite of what you wanted to do, if $results -is [array] you want to iterate over it instead of add it as is to to your $report array.
On the other hand, adding elements to a fixed collection (+=) is a terrible idea.
$dbquery = #'
select name from
sys.databases
where database_id > 4 and name <> 'TEST'"
'#
$result = foreach ($instanceName in $instanceNameList) {
$params = #{
SqlInstance = $InstanceName
Database = "master"
Query = $dbquery
}
$dbNames = Invoke-DbaQuery #params
foreach ($database in $dbNames.Name) {
$params.Database = $database
$params.Query = $query
Invoke-DbaQuery #params | Select-Object #(
'ServerName'
'DBName'
'SchemaName'
'TableName'
'Rgt8'
'Rgt8Date'
'OverOneYearOld'
'Drop_Table'
)
}
}
$result | Format-Table

Execute concurrent processes using PowerShell

I would like to setup a new PowerShell script that invokes my Database Stored Procedure concurrently. I am currently having a Control table that has a Job_ID column and a Code column. There might be more than one Job_ID for a code value in the Control table. Based on the code value I pass in the PowerShell along with a date, I would like the PowerShell to trigger the Stored Procedure which is expecting "Job_ID" and "MyDate" as input parameters.
FYI, I am using PowerShell and SQL Server 2016.
PS C:\PowerShell> $PSVersionTable.PSVersion
Major Minor Build Revision
----- ----- ----- --------
2 0 -1 -1
Here is some sample data for your reference:
CREATE TABLE control_table(JOB_ID INT, CODE VARCHAR(5));
INSERT INTO control_table(1, 'ABC');
INSERT INTO control_table(2, 'ABC');
INSERT INTO control_table(3, 'ABC');
INSERT INTO control_table(1, 'DEF');
INSERT INTO control_table(1, 'GHI');
CREATE PROCEDURE myschema.run_job (#JOB_ID INT, #MyDate DATE)
AS
BEGIN
-- Do Something
END
When I run the PowerShell script by passing 'ABC" as code, it should execute all the three jobs concurrently by reading the control table.
Something like
.\test.ps1 –MyCode “ABC” –Dt “12/27/2018”
As an alternative to jobs you can use async methods of built-in SQL client. Below is the sample code. I assume you already has some "run_job" procedure that can execute other procedures (jobs) by id.
$code = "ABC"
$date = "2018-12-31"
$jobs = #{} # this will store results of async jobs
$str = "Server = YourServer; Database = YourDB; Integrated Security = True;"
#--------
function Async-Sql { param($connStr, $sql, [switch]$GetDataTable)
$conn = New-Object System.Data.SqlClient.SqlConnection $str
$cmd = $conn.CreateCommand()
$conn.Open()
$cmd.CommandText = $sql
if($GetDataTable) {
$dt = New-Object System.Data.DataTable "result"
$r = $cmd.ExecuteReader()
$dt.Load($r)
$conn.Close()
return #(,$dt)
} else {
$w = $cmd.ExecuteNonQueryAsync()
return [PSCustomObject]#{result=$w; conn = $conn} }
}
# ---------------------------------------------
# get a list of jobs from your control table, this will run synchronously
$jobList = Async-Sql -connStr $str -sql "select job_id, code from test.control_table where code = '$code'" -GetDataTable
# main loop. You should call your stored procedure here. Each iteration will create a new connection and execute command asynchronously
foreach($id in $jobList.job_id) {
$command = "EXEC run_job $id, $date"
$r = Async-Sql -connStr $str -sql $command
$jobs.Add( $id, $r )
}
# wait for all jobs to complete
while ($False -in $jobs.Values.result.isCompleted) { sleep -Milliseconds 500 }
# print results / close connections. If you see status as RanToCompletion the job is completed successfuly
foreach($j in $jobs.Keys) {
$res = $jobs[$j].result
[PSCustomObject]#{JobId=$j; isCompleted = $res.isCompleted; Status = $res.Status; result = $res.Result }
$jobs[$j].conn.close()
}
Since you have powershell V2, I'm adding a solution with PS Jobs. Save the code below as sqlExec.ps1:
param($connStr, $sql)
$conn = New-Object System.Data.SqlClient.SqlConnection $connStr
$cmd = $conn.CreateCommand()
$conn.Open()
$cmd.CommandText = $sql
$r = $cmd.ExecuteNonQuery()
$conn.Close()
return $r
Then use this code as a master script:
$str = "Server = YourServer; Database = YourDB; Integrated Security = True;"
$code = "ABC"
$date = (get-date).ToString("yyyy-MM-dd")
$execSript = "path\to\sqlExec.ps1"
# get a list of ids for code. You can achive the same with Invoke-sqlcmd or similar cmdlet.
$conn = New-Object System.Data.SqlClient.SqlConnection $str
$cmd = $conn.CreateCommand()
$conn.Open()
$cmd.CommandText = "select job_id, code from test.control_table where code = '$code'"
$dt = New-Object System.Data.DataTable "result"
$r = $cmd.ExecuteReader()
$dt.Load($r)
$conn.Close()
# main loop
$jobs = #{}
foreach($id in $dt.job_id) { $top = $id*3
$sql = "EXEC run_job $id, $date"
$jobs.Add($id,(Start-Job -FilePath $execSript -ArgumentList $str, $sql))
}
# Wait for jobs and get result
$jobs.Values | Wait-Job | Receive-Job
If you have Invoke-Sqlcmd module you can use it for sql code execution (instead of creating $conn,$cmd,etc)

How to use variable in SQL from powershell script

I am trying to run a SQL from Power Shell(which is on my windows 7 64 bit desktop) and the remote database host is MS SQL Server 2012.
The code is:
$var1 = 'string';
function Get-ODBC-Data{
param(
[string]$query=$('
SELECT COUNT(*)
FROM [master].[sys].[table_name]
WHERE col2 = ''$var1''
;
'),
[string]$username='db_user_name',
[string]$password='db_password'
)
$conn = New-Object System.Data.Odbc.OdbcConnection
$conn.ConnectionString = "DRIVER={SQL Server};Server=123.456.78.90;Initial Catalog=master;Uid=$username;Pwd=$password;"
$conn.open()
$cmd = New-object System.Data.Odbc.OdbcCommand($query,$conn)
$ds = New-Object system.Data.DataSet
(New-Object system.Data.odbc.odbcDataAdapter($cmd)).fill($ds) | out-null
$conn.close()
$ds.Tables[0]
}
$result = Get-ODBC-Data
Write-Host "SQL_Output: " $result[0];
If I use 'string' in the SQL's where clause instead of $var1 then te script works fine and gives expected result.
Quetion
But I want to be able to pass any string as $var1 to the script as parameter. Then use it in the where clause of the SQL. How can I achieve this?
What I tried
I have tried to enclose $var1 in 1,2 or 3 single quotes in the where clause in attempt to escape the single quote. Also tried adding/removing single quote from 'string' when $var1 is assigned value. I did try [string]$var1 = 'string' as well but none of these worked and I keep getting error mostly related to SQL syntax.
Try this:
function Get-ODBC-Data{
param(
[string]$query=$("
SELECT COUNT(*)
FROM [master].[sys].[table_name]
WHERE col2 = '$($var1)'
;
"),
[string]$username='db_user_name',
[string]$password='db_password'
)
$conn = New-Object System.Data.Odbc.OdbcConnection
$conn.ConnectionString = "DRIVER={SQL Server};Server=123.456.78.90;Initial Catalog=master;Uid=$username;Pwd=$password;"
$conn.open()
$cmd = New-object System.Data.Odbc.OdbcCommand($query,$conn)
$ds = New-Object system.Data.DataSet
(New-Object system.Data.odbc.odbcDataAdapter($cmd)).fill($ds) | out-null
$conn.close()
$ds.Tables[0]
}
$result = Get-ODBC-Data
Write-Host "SQL_Output: " $result[0];
The following runs fine on my setup, and shows the correct results:
$var1 = "test22"
function Get-ODBC-Data{
param(
[string]$query=$("
SELECT COUNT(*)
FROM [master].[sys].[table_name]
WHERE col2 = '$($var1)'
;
"),
[string]$username='db_user_name',
[string]$password='db_password'
)
return $query
}
$result = Get-ODBC-Data
Write-Host " ################### Query ######################## "
Write-Host $result
However, you may have a much easier time just passing the entire query into the function as a parameter rather than just one variable part of the query.
Or setting it inside the function and passing $var1 as a mandatory parameter like so:
function Get-ODBC-Data{
param(
[parameter(Mandatory=$true)][string]$var1,
[string]$username='db_user_name',
[string]$password='db_password'
)
$query="
SELECT COUNT(*)
FROM [master].[sys].[table_name]
WHERE col2 = '$($var1)'
;"
return $query
}
$result = Get-ODBC-Data -var1 "working"
Write-Host " ################### Query ######################## "
Write-Host $result

Cannot update SQL Server table from powershell

I have created a test database in SQL Server 2016 Express, it holds 1 table labeled drivers.
I use PowerShell to perform ciminstance query of installed drivers, then insert those values into the test database driver table. (the insert works as expected)
The issue I have is attempting to update the driver table, only the last object is inserted into the database 40 times(that is how many drivers are returned from the ciminstance query). I have created 2 PowerShell scripts
Insert values
Update values
Stumped!
$database = 'test'
$server = 'groga\sqlExpress'
$table = 'dbo.Driver'
$SQLServer = "groga\sqlExpress"
$SQLDBName = "test"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database =
$SQLDBName; Integrated Security = True"
$SqlConnection.Open()
$today = Get-Date
$drivers = gcim win32_pnpsigneddriver -Property *
$model = gcim win32_computersystem -Property *
foreach($driver in $drivers)
{
if(!($driver.Description -match "Generic") -and $driver.Manufacturer -
notmatch 'Microsoft|Standard|Generic' -and $driver.DriverDate -ne $null)
{
$count = New-Object psobject -Property #{
'Date' = $driver.DriverDate
'Manufacturer' = $driver.Manufacturer
'Version' = $driver.DriverVersion
'PackageID' = "0"
'SKU' = $model.SystemSKUNumber
'Model' = $model.Model
'Today' = $today}
$col1 = $count.Date
$col2 = $count.Manufacturer
$col3 = $count.Version
$col4 = $count.PackageID
$col5 = $count.SKU
$col6 = $count.Model
$col7 = $count.Today
$update = #"
UPDATE $table
SET [Date]='$col1',
[Manufacturer]='$col2',
[Version]='$col3',
[PackageID]='$col4',
[SKU]='$col5',
[Model]='$col6',
[Today]='$col7'
"#
$dbwrite = $SqlConnection.CreateCommand()
$dbwrite.CommandText = $update
$dbwrite.ExecuteNonQuery()
}
}
$Sqlconnection.Close()
The UPDATE statement will apply to all rows that are matched by the query. So what your script is doing is setting ALL rows in the table to info for a driver then doing the same for the whole list.
You will need to determine the fields which uniquely identify each driver and then filter your query down to that. Looking at sample driver info, this could be Date, Manufacturer, Device Name (something you would need to add to your schema), DriverVersion.
Example with just Date, Manufacturer, DriverVersion:
$update = #"
UPDATE $table
SET [PackageID] = '$col4'
[SKU]='$col5',
[Model]='$col6',
[Today]='$col7'
WHERE [Date] = '$col1' AND [Manufacturer]='$col2' AND [Version]='$col3'
"#

How do I get around this error in PowerShell? Exception setting "Rtf": "Error creating window handle."

This has no issue with SQL. it's when creating the rtf object.
I am connecting to a sql database and pulling information. Some of the information is html,rtf, and plain text. After about 10 mins of running I get this:
Exception setting "Rtf": "Error creating window handle."
At line:24 char:76
+ ... Name System.Windows.Forms.RichTextBox; $rtf.Rtf = $convo.Body; $body ...
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
Has anyone else ran into this issue?
Here is the script itself.
#Who are you searching for?
#Example User ID: user#domain.com
$Subject = "changeme#domain.com"
#Set the date to search from
#Example date format: 2016-08-16.
#Leave it blank if you don't want to search for just dates.
$Date = ""
#Blank array to store the conversation history
$arr = #()
#Lync Archive Server
$SQLSvr = "ServerName Goes Here"
#Lync Archive Database
$Database = "LcsLog"
#Get the UserId's
$UserUri = Invoke-Sqlcmd -Query "Select UserUri,UserId From dbo.Users u;" -ServerInstance $SQLSvr -Database $Database
#Build the Select Statement
$select = "Select * from dbo.Users d left join dbo.Messages m on FromId = d.UserId or ToId = d.UserId Where d.UserUri = '$Subject' "
if($Date)
{
$select = $select +"and m.MessageIdTime >= '$Date 00:00:01.550' order by m.MessageIdTime asc;"
}
else
{
$select = $select + "order by m.MessageIdTime asc;"
}
#Get the conversation history
$ConvoData = Invoke-Sqlcmd -Query ($select) -ServerInstance $SQLSvr -Database $Database;
#Loop through each conversation
foreach($convo in $ConvoData)
{
#Loop through each user.
foreach($user in $UserUri)
{
#Verify the FromId
if($convo.FromId -eq $user.UserId)
{
$FromID = $user.UserUri
}
#Verify the ToId
if($convo.ToId -eq $user.UserId)
{
$ToId = $user.UserUri
}
}
#Parse the body for legible reading
switch ($convo.ContentTypeId)
{
'1' {$html = New-Object -ComObject "HTMLFile"; $html.IHTMLDocument2_write($convo.Body);$body = $html.IHTMLDocument2_body.innerText; $html.close();}
'2' {$rtf = New-Object -TypeName System.Windows.Forms.RichTextBox; $rtf.Rtf = $convo.Body; $body = $rtf.Text; $rtf.Clear();}
'3' {$body = $convo.Body}
}
#Build the Message Output
$obj = New-Object -TypeName psobject -Property #{User = $Subject; "Message Time" = $convo.MessageIdTime; From = $FromID; To = $ToId; Body = $body}
#Add data to the array
$arr += $obj
}
$arr | Select User,"Message Time",From,To,Body | Export-csv "$env:userprofile\desktop\$Subject - conversation report.csv"
Not really an answer, but a recommendation that you should turn your parameters into a Param block. It would be more useful if you wanted to call the script from the command line or turn it into a function.
Param (
# Parameter Subject
[Parameter(Mandatory = $true,
HelpMessage = 'Who are you searching for? e.g. User ID: user#domain.com')]
$Subject = 'changeme#domain.com',
# Parameter Date
[Parameter(HelpMessage = 'Set the date to search from. e.g. "2016-08-16"')]
[String]
$Date,
# Parameter SQLSvr
[Parameter(Mandatory = $true,
HelpMessage = 'ServerName Goes Here')]
$SQLSvr,
# Parameter Database
[Parameter(Mandatory = $true,
HelpMessage = 'Lync Archive Database')]
$Database = 'LcsLog'
)
I figured it out. By creating one instance of my RTF object at the beginning it corrected my error.
#Who are you searching for?
#Example User ID: user#domain.com
$Subject = "changeme#domain.com"
#Set the date to search from
#Example date format: 2016-08-16.
#Leave it blank if you don't want to search for just dates.
$Date = ""
#Blank array to store the conversation history
$arr = #()
#Create RTF and HTML Objects
$html = New-Object -ComObject "HTMLFile";
$rtf = New-Object -TypeName System.Windows.Forms.RichTextBox;
#Lync Archive Server
$SQLSvr = "Server Name goes here"
#Lync Archive Database
$Database = "LcsLog"
#Get the UserId's
$UserUri = Invoke-Sqlcmd -Query "Select UserUri,UserId From dbo.Users u;" -ServerInstance $SQLSvr -Database $Database
#Build the Select Statement
$select = "Select * from dbo.Users d left join dbo.Messages m on FromId = d.UserId or ToId = d.UserId Where d.UserUri = '$Subject' "
if($Date)
{
$select = $select +"and m.MessageIdTime >= '$Date 00:00:01.550' order by m.MessageIdTime asc;"
}
else
{
$select = $select + "order by m.MessageIdTime asc;"
}
#Get the conversation history
$ConvoData = Invoke-Sqlcmd -Query ($select) -ServerInstance $SQLSvr -Database $Database;
#Loop through each conversation
foreach($convo in $ConvoData)
{
#Loop through each user.
foreach($user in $UserUri)
{
#Verify the FromId
if($convo.FromId -eq $user.UserId)
{
$FromID = $user.UserUri
}
#Verify the ToId
if($convo.ToId -eq $user.UserId)
{
$ToId = $user.UserUri
}
}
#Parse the body for legible reading
switch ($convo.ContentTypeId)
{
'1' {$html.IHTMLDocument2_write($convo.Body);$body = $html.IHTMLDocument2_body.innerText; $html.close();}
'2' {$rtf.Rtf = $convo.Body; $body = $rtf.Text; $rtf.Clear();}
'3' {$body = $convo.Body}
}
#Build the Message Output
$obj = New-Object -TypeName psobject -Property #{User = $Subject; "Message Time" = $convo.MessageIdTime; From = $FromID; To = $ToId; Body = $body}
#Add data to the array
$arr += $obj
}
$arr | Select User,"Message Time",From,To,Body | Export-csv "$env:userprofile\desktop\$Subject - conversation report.csv"