How to use IN clause with SQLParameters? - sql

My function works perfectly if I provide one instance of ComputerName value. My question is how to modify SQL query so it could accept an array? I don't believe creating a loop to query a database several times is a proper way. For example, I think this SQL query with IN clause is a proper way:
SELECT * FROM Customers WHERE Country IN ('Germany', 'France', 'UK');
In other words, I'd like to be able to call this function providing multiple ComputerName values. Like this:
PS C:\>Get-Query_Database_Query1('comp01','comp02')
The code I need help with. Please use SQLParameters to build SQL query:
function Get-Query_Database_Query1
{
[OutputType([System.Data.DataTable])]
param
(
[Parameter(Mandatory = $false,
Position = 1)]
[string]$PCname
)
#Database Query
$QueryString = "select * from [Table1] where [ComputerName]=#PCname"
#Database Connection String
$ConnectionString = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Open\Database4.mdb;Password=;User ID=Admin'
$command = New-Object System.Data.OleDb.OleDbCommand ($QueryString, $ConnectionString)
$Command.Parameters.Add("#PCname", [System.Data.OleDb.OleDbType]::VarChar, 50).Value = $PCname;
$adapter = New-Object System.Data.OleDb.OleDbDataAdapter ($command)
#Load the Dataset
$dataset = New-Object System.Data.DataSet
[void]$adapter.Fill($dataset)
#Return the Dataset
return #( ,$dataset.Tables[0])
}

You need to do a little bit of string manipulation, also when working with SQL queries is a lot easier to use Here Strings.
If you are going to pass multiple computers to your functions, the parameter $PCname has to be able to accept an array of strings, hence changing [string] to [string[]].
Check out this code and see if it works for you:
function Get-Query_Database_Query1
{
[OutputType([System.Data.DataTable])]
param
(
[Parameter(Mandatory = $false,
Position = 1)]
[string[]]$PCname
)
#Database Query
$QueryString = #"
SELECT *
FROM [Table1]
WHERE [ComputerName] IN ('{0}')
"# -f ($PCname -join "','")
$QueryString
}
Get-Query_Database_Query1 -PCname 'computer1','computer2','computer3','computer4'
Here is how the query should look like:
PS /home/> Get-Query_Database_Query1 -PCname 'computer1','computer2','computer3','computer4'
SELECT *
FROM [Table1]
WHERE [ComputerName] IN ('computer1','computer2','computer3','computer4')

Related

How do I pass an variable/array member to a SQL query in PowerShell? [duplicate]

This question already has answers here:
How can you use an object's property in a double-quoted string?
(5 answers)
Closed 9 months ago.
This has probably been answered somewhere before, but I can't figure out how to ask google the right question. To be able to define fewer variables, I would like to be able to pass a member of a variable or array to a SQL query. As an example, I would like to be able to define something like $date = get-date and use $date.month to just pass just the month in the query.
The issues I run into is that the period used to define the member seems to break things in a SQL query. Is there a way to properly punctuate this type of variable in this situation?
For fuller context:
$ConnectionString = "Server=" + $TargetServer + ";Database=" + $TargetDatabase + ";Trusted_Connection=$true;";
$TargetConnection = New-Object System.Data.SqlClient.SqlConnection($ConnectionString);
$TargetConnection.Open();
$date = get-date
$sql = "SELECT *
FROM [database].[dbo].[table]
where ([MONTH] = $date.month and [YEAR] = $date.year)"
$TargetCommand = New-Object System.Data.SqlClient.SqlCommand($sql, $TargetConnection);
$TargetCommand.ExecuteScalar()
$TargetConnection.Close()
The month and year columns have the values stored as int.
As I mentioned, what you have above injection, not parametrisation. You don't tell us why what you have isn't working, so this is some what of a guess, but most likely you want something like this:
$connectionString = 'Server=' + $TargetServer + ';Database=' + $TargetDatabase + ';Trusted_Connection=$true;'
$sqlConn = New-Object System.Data.SqlClient.SqlConnection($ConnectionString)
#Start a try, so that we can close the connection on error
try{
$sqlConn.Open()
$sqlCmd = $sqlConn.CreateCommand()
$sqlCmd.Connection = $sqlConn #Assign the connection to the command
$query = "SELECT *
FROM [dbo].[table] --Database isn't needed, you have defined it in your connection string
WHERE [MONTH] = #Month and [YEAR] = #Year;" #Define the query
$sqlCmd.CommandText = $query
$date = get-date #Get the current date
$sqlCmd.Parameters.Add("#Month", 8).Value = $date.month #Add Month Parameter
$sqlCmd.Parameters.Add("#Year", [System.Data.SqlDbType]::Int).Value = $date.year #Add year Parameter
#I don't know what you want to do with the data, so I put it into a data adapter
$sqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter $sqlcmd
$dataSet = New-Object System.Data.DataSet
$sqlAdapter.Fill($dataSet)
}
finally{
if ($sqlAdapter -ne $null) { $sqlAdapter.Dispose(); }
if ($sqlConn -ne $null) { $sqlConn.Close(); }
}
$dataSet.Tables
You can get the list of the enum values for the parameter types in the documentation; note I use 8 in the above for int. AlwaysLearninghas since reminded me of the correct syntax, so I now demonstrate both. #Month passes uses an enum value, an #Year uses the name of the datatype from sqlDbType.
From the comments it seems like you want to inject; in some ways this defeats of the object of using the .Net objects. If you simply want to inject the data, the you could just use Invoke-SqlCmd:
Invoke-Sqlcmd -Query "SELECT * FROM dbo.[table] WHERE Month = $($(get-date).month) AND [Year] = $($(get-date).year);" -ServerInstance $TargetServer -Database $TargetDatabase

Powershell insert into MS Access with WHERE clause

Trying to insert values into MS Access DB based on values entered into a powershell form with a WHERE clause. I'm receiving a simple error but struggling to resolve ("Missing Semicolon (;) at end of SQL Statement")
Here is my base code;
$query = "INSERT INTO SignIns ([DateTimeOUT], [SignedOut]) VALUES ('$($info.F1)','$($info.F2)') FROM $Info WHERE SignIns.Surname = '$($Info.F3)'"
$cmd = $conn.CreateCommand()
$cmd.CommandText = $query
$result = $cmd.ExecuteNonQuery()
$conn.Close()
I've amended to add a semicolon in all places I thought could resolve, but no luck, still returns the same error (Missing Semi Colon at end of SQL statement);
$query = "INSERT INTO SignIns ([DateTimeOUT], [SignedOut]) VALUES ('$($info.F1)','$($info.F2)') FROM $Info WHERE SignIns.Surname = '$($Info.F3);';";
$cmd = $conn.CreateCommand()
$cmd.CommandText = $query;
$result = $cmd.ExecuteNonQuery();
$conn.Close()
(for reference, I've added a semi-colon at the end of my WHERE clause, at the end of the $Query variable and tried to append onto the end of $query when executing in the $cmd.commandtext variable, and also on the end of the $result variable.
I expect the statement to execute as normal and update with the given values. Testing within Access DB itself is difficult as I am unable to reference my PS form from within the DB. Any help greatly appreciated,
Thanks.
Update: Ameding query to UPDATE now lets me 'insert' values with WHERE statement following a simple logic.
$conn.Open()
$query = "UPDATE SignIns SET DateTimeOUT = '$($info.F1)' WHERE SignIns.Surname = '$($Info.F3)'";
$cmd = $conn.CreateCommand()
$cmd.CommandText = $query;
$result = $cmd.ExecuteNonQuery();
$query = "UPDATE SignIns SET SignedOut = '$($info.F2)' WHERE SignIns.Surname = '$($Info.F3)'";
$cmd = $conn.CreateCommand()
$cmd.CommandText = $query;
$result = $cmd.ExecuteNonQuery();
$conn.Close()
It is not a method I'm normally use to when inputting new values into a table, but same result so.. I don't think there's any implications. It probably takes the update as 'Update from NULL to VALUE' as opposed to INSERT FROM source to DESINTATION (where)

Sql query result as an array of objects powershell

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.

How to return the value of a row, without column name in a query?

I am writing a Powershell script that extracts data via the SQLPS module, executing a query directly to the SQL Server. If I do a plain
Select <column A> from <table B>
I get the column listed as well, as stated like this:
Column A
--------
Value C
Here I wish to only retrieve the Value C, for storing it as a variable.
If you are not bound to use this SQLPS module then this might be a easier way to do it:
$connection = new-object System.Data.SqlClient.SqlConnection("Data Source=.;Initial Catalog=TestDB;Integrated Security=True");
$connection.Open()
$query = "SELECT [A] FROM [dbo].[Tablename]"
$cmd = new-object "System.Data.SqlClient.SqlCommand" ($query, $connection)
$cmd.CommandTimeout = 0
$executeReader = $cmd.ExecuteReader()
while ($executeReader.Read()) {
$Name = $executeReader.GetValue(0)
//Do what you desire with the resultset.
$Name + "`r`n" >> D:\PathToResultFolder\result.txt
}
$executeReader.Close()
$connection.Close()
Also I read and think that this should be handled outside of the Query as it is not normal for a Query to not show column-names.

Returning array from sql query function

I'm trying to return an array from a function that query a sql table.
All ok but only if the elements of array is more than one. If the array is composed by only one element the function doesn't return a type array but a type string. I'm a newbie to powershell and so confused...
Here my function :
Function test2{
$query = #"
SELECT DfsTgtPath
FROM DfsData_v2 WHERE TgtSrvName = 'compname' AND DfsTgtPath LIKE
'%string%' ORDER BY DfsTgtPath"#
$connection = new-object system.data.sqlclient.sqlconnection("server=SQLSERV;database=DB;trusted_connect>i on=true;" )
$adapter = new-object system.data.sqlclient.sqldataadapter ($query,$connection)
$table = new-object system.data.datatable
$recs = $adapter.Fill($table)
$aDfsTgtPath = #($table | select -ExpandProperty DfsTgtPath)
return #($aDfsTgtPath)
}
$result = test2
I would expect the $result as an array containing one single string element but it's seems to be not an array but of type System.String.