Powershell select where not in - powershell

I have a Powershell script where I select all the values (they are numeric) from the column of a tab in an Excel sheet. It works but I want to exclude numbers that are returned from a select from an Oracle table. For the moment I just want to get the select "where not in" functioning, I will worry about putting the values I want excluded into an array later.
My Powershell select is below
$Table = "Inventory Parts$"
$qry = "select [Part No] from [{0}]" -f $Table;
$cmd.CommandText = $qry;
$da.SelectCommand = $cmd;
$dt = new-object System.Data.dataTable("$($Table)");
$null = $da.fill($dt);
I select from the tab (Inventory Parts) and assign it to a data table. How do I put in "where not in" to the select? If I could just have an array hardcoded with values and use that as the values for "where not in" it would be start. After that I will instead populate the array from the Oracle table.
Thank you for any replies.
Edit:
I have got as far as populating an array with the values I want in the "where not in" clause (see below).
$queryString = "select PART_NO from INVENTORY_PART_CFV WHERE PART_NO like
'100009%' "
$array = New-Object System.Collections.ArrayList
$command = new-Object
System.Data.OracleClient.OracleCommand($queryString, $connection)
$connection.Open()
$rdr = $command.ExecuteReader();
Foreach($row in $rdr)
{
$array.add( $row.Item("PART_NO") )
}
They go into the array "$array" but I need to append this on to my select statement
"select PART_NO from INVENTORY_PART_CFV WHERE PART_NO NOT IN " + $array
I don't know how to do this though.

Related

Powershell: How to pick 1 result of many from Invoke-Sqlcmd Select statement

When I run a SELECT statement using Invoke-Sqlcmd, I'd like to choose 1 out of X results to move forward with a loop. In the example below, I have multiple results for 'Bob' and I'd like to choose the appropriate one to continue working with the query to update his favFood (I know I don't have the code there but just giving an example of what I have so far)
$nameList = $(Write-Host "Enter a list of Names seperated by a comma (ex.`"Bob, Rick, Jordan\`"): " -ForegroundColor Green -NoNewline; Read-Host)
$nameList = $nameList.Split(', ')
ForEach ($element in $nameList)
{
Invoke-Sqlcmd -Server Server01 -database People -Query ` "SELECT firstName, lastName, address, favFood`
FROM People ` WHERE firstName = '$element'" }
Is there a way I can list a first name and have the program ask me which of X results I'd like to update?
I'm honestly a bit stumped on how to choose the specific row/person out of X results to update.
Small side-note/bonus points: When running the program it complains about a "Missing argument in parameter list." as I'm writing down the names even though it will accept and return results as expected (I am separating the names by a space and comma ex. "Bob, Ross, Jay"
In theory you don't need a loop at all, you could use the SQL IN operator and pass all the names of $nameList to your query. Then once you have stored the $result of your query, you can use Out-GridView -PassThru to have a popup DataGridView for you to choose a row, which would be stored in $choice.
Write-Host 'Enter a list of Names seperated by a comma (ex."Bob, Rick, Jordan"): ' -ForegroundColor Green -NoNewline
$nameList = (Read-Host).Split(',').Trim()
# $namelist would become Bob','Rick','Jordan
$nameList = $nameList -join "','"
$result = Invoke-Sqlcmd -Server Server01 -Database People -Query #"
SELECT firstName, lastName, address, favFood
FROM People WHERE firstName IN ('$namelist');
"#
$choice = $result | Out-GridView -PassThru

PowerShell SQL Query to Update Table

I am using PowerShell to run a SQL query. I then want to update another table based on information pulled from the query. I have tested my SQL query and update statements directly in the SQL Server Management Studio so I know that they work. The results of those tests show that there are over 800 records that should be updated. However, when I run the same query and update from within PowerShell, it only updates one record. I have mostly copied this script from another much larger script that was written in a similar format. But it appears that I am missing a ForEach loop (or something similar) but cannot figure out where to place it or how. Here is my script:
# Set the database connection strings
$Conn02 = new-object System.Data.SqlClient.SqlConnection "SERVER_INFORMATION"
$mySQL02 = New-Object System.Data.SqlClient.SqlCommand
$mySQL02.Connection = $Conn02
$Conn03 = new-object System.Data.SqlClient.SqlConnection "SERVER_INFORMATION"
$mySQL03 = New-Object System.Data.SqlClient.SqlCommand
$mySQL03.Connection = $Conn03
#Connect to the database to perform the query
$Conn02.Open()
$mySQL02.CommandText = "SELECT IDNUM, FNAME, LNAME
FROM TABLE1
WHERE STATUS = 'C'"
$SQL02 = $mySQL02.ExecuteReader()
WHILE($SQL02.Read()){
$NEWID = $SQL02['ID_NUM']
$FNAME1 = $SQL02['FNAME']
$LNAME1 = $SQL02['LNAME']
}
#Run the update
$Conn03.Open()
$mySQL03.CommandText =
"INSERT INTO TABLE2 (user_id,firstname,lastname)
VALUES ('$NEWIDme','$FNAME1','$LNAME1')"
Thank you for your time
As #jeroen said, you need to move the Insert statement into the while loop. Here is the code should look like:
#Connect to the database to perform the query
$Conn02.Open()
$mySQL02.CommandText = "SELECT IDNUM, FNAME, LNAME
FROM TABLE1
WHERE STATUS = 'C'"
$SQL02 = $mySQL02.ExecuteReader()
#Save reader into datatable
$Datatable = New-Object System.Data.DataTable
$Datatable.Load($SQL02)
#Close the connection
$Conn02.Close()
#Run the update
$Conn03.Open()
foreach($row in $dt){
$NEWID = $row['ID_NUM']
$FNAME1 = $row['FNAME']
$LNAME1 = $row['LNAME']
$mySQL03.CommandText =
"INSERT INTO TABLE2 (user_id,firstname,lastname)
VALUES ('$NEWIDme','$FNAME1','$LNAME1')"
$mySQL03.ExecuteNonQuery()
}
$Conn03.Close()
To prevent SQL Injection I suggest using Parameters when assigning values
foreach($row in $dt){
$NEWID = $row['ID_NUM']
$FNAME1 = $row['FNAME']
$LNAME1 = $row['LNAME']
$mySQL03.CommandText =
"INSERT INTO TABLE2 (user_id,firstname,lastname)
VALUES (#NEWIDme,#FNAME1,#LNAME1)"
$mySQL03.Parameters.Clear()
$Command.Parameters.AddWithValue('#NEWIDme',$NEWID) | Out-Null
$Command.Parameters.AddWithValue('#FNAME1',$FNAME1) | Out-Null
$Command.Parameters.AddWithValue('#LNAME1',$LNAME1) | Out-Null
$mySQL03.ExecuteNonQuery()
}

Insert data with a collection object into a SQL Server table

The code below does not error, it inserts into a SQL Server table with no issues. However the [ServicePrincipalNames] data is not inserted how I planned.
The value that gets inserted into the table is
Microsoft.ActiveDirectory.Management.ADPropertyValueCollection
What I am trying to insert is the value in that object collection which looks like this:
WSMAN/Server1Name
WSMAN/Server1Name.mx.ds.abc.com
TERMSRV/Server1Name
TERMSRV/Server1Name.mx.ds.abc.com
RestrictedKrbHost/Server1Name
HOST/Server1Name
RestrictedKrbHost/Server1Name.mx.ds.abc.com
HOST/Server1Name.mx.ds.abc.com
The code to do the insert is shown here below. How could I change this to have the insert put all the services in the column, separated by |?
$sqlServer='SomeServer'
$catalog = 'SomeDatabase'
$insert = #"
Insert into dbo.ADServers([Name],[OperatingSystem],[OperatingSystemVersion],[ipv4Address],[Created],[Deleted],[whenChanged],[Modified],[Description],[ServicePrincipalNames],[DisplayName],[Location],[DistinguishedName],[DNSHostName])
values('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}', '{13}')
"#
$start = (Get-Date).ToString('MM/dd/yyyy hh:mm:ss tt')
$connectionString = "Data Source=$sqlServer;Initial Catalog=$catalog;Integrated Security=SSPI"
# connection object initialization
$conn = New-Object System.Data.SqlClient.SqlConnection($connectionString)
#Open the Connection
$conn.Open()
# Prepare the SQL
$cmd = $conn.CreateCommand()
#WMI ouput transformation to SQL table
Get-ADComputer -Filter {operatingSystem -Like 'Windows*server*2019*'} -Property * |`
Select Name,OperatingSystem,OperatingSystemVersion,ipv4Address,Created,Deleted,whenChanged,Modified,Description,ServicePrincipalNames,DisplayName,Location,DistinguishedName,DNSHostName |`
forEach-object{
$cmd.CommandText = $insert -f $_.Name, $_.OperatingSystem, $_.OperatingSystemVersion, $_.ipv4Address, $_.Created, $_.Deleted, $_.whenChanged, $_.Modified,$_.Description, $_.ServicePrincipalNames , $_.DisplayName,$_.Location,$_.DistinguishedName,$_.DNSHostName
$cmd.ExecuteNonQuery()
}
$end = (Get-Date).ToString('MM/dd/yyyy hh:mm:ss tt')
Write-Host $start
Write-Host $end
Ok after a more time googling and learning about out-string. In order to display objects i had to create an expression on that column and rewrite it as below. and it worked
In query replace the
ServicePrincipalNames
with
#{Label="ServicePrincipalNames";Expression={$_.ServicePrincipalNames -join ";" }}

Take value from SQL server and delete relevant folders

Im new to powershell and would like to
-delete all rows in a sql server DB that have a date older than 10 years
-for every row that is deleted also delete a folder or a hard disk
So for example if I run the query
DELETE FROM [RMS].[dbo].[requests] where date_logged < DATEADD(year, -10, GetDate())
I then thought I could get the lowest request_id and just delete any folders under that number.
So for example if I delete 10 rows with my delete query and then do a select
It would say that the lowest request_id is 11.
I've started below but I'm not sure how to capture that the oldest request_id is?
The SQL would be this ...
SELECT TOP 1 request_id FROM [RMS].[dbo].[requests] order by request_id asc
And also how I would delete any folder "less" than that value.
So if request_id = 11 then I'd need to delete
C:\temp\1
C:\temp\2
C:\temp\3
...
C:\temp\10
Thanks
P
$connectionString = "Data Source=server;Initial Catalog=RMS;Integrated Security=SSPI";
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString);
$commandR = New-Object System.Data.SqlClient.SqlCommand("DELETE FROM dbo.requests WHERE request_id= 1", $connection);
$commandCount = New-Object System.Data.SqlClient.SqlCommand("select count(*) from requests", $connection);
$connection.Open();
$rowsDeletedR = $commandR.ExecuteNonQuery();
Write-Host "$rowsDeletedR rows deleted";
$rowsCountR = $commandCount.ExecuteScalar();
Write-Host "$rowsCountR rows in requests table";
$connection.Close();
Your task is broad. I intentionally splitted it into smaller pieces. Take a look at this demo and comments.
Since Invoke-SqlCmd is considered harmful (SQL Injection), I use my own function to invoke SQL
function Invoke-Sql(
$ConnectionString,
$Query,
$Parameters
) {
$conn = New-Object System.Data.SqlClient.SqlConnection -ArgumentList $ConnectionString
$cmd = New-Object System.Data.SqlClient.SqlCommand -ArgumentList $Query,$conn
$conn.Open()
if ($Parameters) {
foreach ($arg in $Parameters.GetEnumerator()){
$cmd.Parameters.AddWithValue($arg.Key, $arg.Value) | Out-Null;
}
}
$reader = $cmd.ExecuteReader()
if ($reader.Read()) {
[string[]]$columns = 0..($reader.FieldCount-1) |
% { if ($reader.GetName($_)) { $reader.GetName($_) } else { "(no name $_)" } }
do {
$obj = #{}
0..($reader.FieldCount-1) | % { $obj[$columns[$_]] = $reader[$_] }
[PSCustomObject]$obj
} while ($reader.Read())
}
$reader.Dispose()
$cmd.Dispose()
$conn.Dispose()
}
You need a database table. Since there is no strict schema in question, I assume following, minimal:
$conn = 'Data Source=.;Initial Catalog=Test;Integrated Security=SSPI'
$createTestTable = #'
CREATE TABLE MyRequests
(
RequestId int,
DateLogged datetime
)
'#
Invoke-Sql $conn $createTestTable
There is no sample data, I assume folders named 1, 2, 3, .. 7 and matching records in SQL database:
1..7 | % {
Invoke-Sql $conn 'INSERT MyRequests VALUES (#id,#value)' #{id=$_;value=[DateTime]::Now.AddDays(-$_)}
mkdir $_
}
Table should contain following records (dates may differ):
RequestId DateLogged
----------- -----------------------
1 2018-09-23 14:47:49.113
2 2018-09-22 14:47:49.130
3 2018-09-21 14:47:49.137
4 2018-09-20 14:47:49.140
5 2018-09-19 14:47:49.140
6 2018-09-18 14:47:49.143
7 2018-09-17 14:47:49.147
Then, final solution:
#get deleted id's using OUTPUT clause
$older = Invoke-Sql $conn 'DELETE FROM MyRequests OUTPUT deleted.RequestId WHERE DateLogged<#date' #{date=[DATETime]::Now.AddDays(-4)}
#foreach id in returned set, delete corresponding folder
$older | select -ExpandProperty RequestId | % { rm $_ }

Powershell: How to enclose an array of strings with quotes so that SQL "IN" operator works?

I'm basically asking users to enter some formIds separated by a comma and passing that string to a function which does a SQL query update. The problem is I'm not able to use the IN operator since the query has to be in the format: "(''),('')".
I want to make this query work:
UPDATE [dbo].[Details] SET Field1 = 0 WHERE FormId in ('123','456','789')
And for this I want to accept this user input:
123,456,789
How can I get this to work?
Function Execute-Query([String[]] $FormIds)
{
$arr = $FormIds -split ','
ForEach ($formId in $arr)
{
echo $formId
}
$dataSource = "x"
$user = "x"
$pwd = ""x
$database = "x"
$connectionString = "Server=$dataSource;uid=$user; pwd=$pwd;Database=$database;Integrated Security=False;"
$sqlcmd = new-object "System.data.sqlclient.sqlcommand"
$sqlcmd.connection = $con
$sqlcmd.CommandTimeout = 600000
$sqlcmd.CommandText = “UPDATE [dbo].[Details] SET Field1 = 0 WHERE FormId in (????)”
$rowsAffected = $sqlcmd.ExecuteNonQuery()
$connection.Close()
}
$strFormIds = Read-Host "Enter FormIds:"
Execute-Query $strFormIds
Not sure how IN comes into play, but assuming you just want to construct a query string as in your example, use the following subexpression instead of ???? in your code:
$("'" + ($FormIds -join "','") + "'")
That said, your Read-Host call will not automatically convert your '123','456','789' user input into an array of individual strings - instead, it will create a single-element array whose only element is that very string as a whole.
You must therefore explicitly parse your single-string user input into an array first, as follows:
[string[]] $formIds = $strFormIds -split '[, ]' -ne ''
Note the set of characters to split by - , and <space> - so that both 123,456 and 123, 456 or even 123 456 would work. The -ne '' part filters out empty elements that result from multiple adjacent spaces, for instance.
If you are passing the id's to your method with double quotes like shown below, can't you just use the $FormIds variable directly (assuming you have validated it and removed potential sql injection before of course)?
Function Execute-Query([String[]] $FormIds)
{
$query = "UPDATE [dbo].[Details] SET Field1 = 0 WHERE FormId in ($FormIds)"
$sqlcmd.CommandText = $query
Write-Host $query
}
Execute-Query "'123','456','789'"
This should output:
UPDATE [dbo].[Details] SET Field1 = 0 WHERE FormId in ('123','456','789')