Just wondering how i could add a sql script inside of a powershell script.
Right now I have a script that gets this sql file and run for each database. I was wondering if I could include this sql script inside of my powershell . I am planning to automate my powershell script to run daily.
Here is what i have at the moment
$out = foreach ($SERVER in $SERVERS) {
$InvokeParams = #{
Server = $SERVER
Database = 'test database'
Username = 'username'
Password = 'password'
InputFile = 'C:user\test.sql'
QueryTimeout = 120 #avoid query to break
}
Goal: Try to implement sql code inside of INPUTFILE instead of calling/giving a path for a sql file
Goal:
InputFile = SQL SCRIPT
I can only give an example since I don't know the contents of the sql script.
$out = foreach ($SERVER in $SERVERS) {
$InvokeParams = #{
Server = $SERVER
Database = 'test database'
Username = 'username'
Password = 'password'
Query = "Select Column1,Column2 from Table"
QueryTimeout = 120 #avoid query to break
}
Invoke-Sqlcmd #InvokeParams
}
You would just need to add your query to the Query key in your $InvokeParams hash table. If you want to keep your hash table neater, you can always type the query as a string and set it to another variable. Then use Query = $variable in your hash table. If the query is long (multi-lined) and has other quotes in it, you can use a here-string (#''# or #""#) for simplification.
$Query = #'
USE Database
Select Table1.Column1,Table1.Column2,Table2.Column1
FROM Table1
JOIN Column1 ON Table2.Column1=Table1.Column1
GO
'#
$out = foreach ($SERVER in $SERVERS) {
$InvokeParams = #{
Server = $SERVER
Database = 'test database'
Username = 'username'
Password = 'password'
Query = $Query
QueryTimeout = 120 #avoid query to break
}
Invoke-Sqlcmd #InvokeParams
}
Related
Trying to run the following query in multiple databases but receiving the following error : Invoke-SqlCmd : Value cannot be null.
$query = #' SQL SCRIPT ' #
$csvFilePath = 'PATH FOR CSV FILE'
$serverlist = echo (list of servers )
$queryresults = $null
$InvokeParams = #{
SERVER = $serverlist
Database = 'TEST DATABASE'
Username = 'username '
Password = 'password1'
Query = $query
QueryTimeout = 100
}
foreach($server in $serverlist)
{
write-host "Executing query against server: " $server
$queryResults = Invoke-SqlCmd #InvokeParams -Erroraction stop
}
write-host "Saving Query Results in CSV format..."
$queryResults | export-csv $csvFilePath -NoTypeInformation
write-host "Converting CSV output to Excel..."
Do I always have to specify parameters ? What if my query is about creating a table for example ?
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)
I am trying to perform an insert to the Oracle database using Powershell script. This approach works fine for a select query but for insert it gives an error. I read lot of Stack Overflow posts and various other sites and tried various methods but none of them are working.
How can I debug this? I am no expert in Powershell. I am quite sure there is some issue with the SQL.
Add-Type -Path "P:\Home\Full Oracle\ora11g_x86\odp.net\bin\2.x\Oracle.DataAccess.dll"
try
{
$compConStr = "Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=**)(PORT=1552)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=**)));User Id=**;Password=**;"
$oraConn= New-Object Oracle.DataAccess.Client.OracleConnection($compConStr)
$sid = "0001"
$region = "CH"
$timestamp = "2017-04-20 14:14:00"
$dep = "17-04-2017"
$scenario = "A"
$milestone = "ASB_XREF_GLCC_LOAD_2ND_END_E"
$finishtime = "2017-04-18/11:11"
$sql = "INSERT INTO APP_PDM_MART.PDM_GEAR_KPI_REP_SNOW(ST_ID,PRIORITY,STATUS,ACTIVE,ACTIVITY_DUE,ADD_REPORTED_BY,BUSINESS_CRITICALITY) VALUES($sid,$region,$timestamp,$dep,$scenario,$milestone,$finishtime)"
$oraConn.Open()
$command = New-Object Oracle.DataAccess.Client.OracleCommand($sql,$oraConn)
$tx = $oraConn.BeginTransaction()
$command.ExecuteNonQuery()
For me it looks like the server doesn't like the spaces in your variables. Wihout adding quotes to the variables it won't look like a connected string in the SQL Statement. Try defining your variables like this:
$sid = "`'0001`'"
$region = "`'CH`'"
$timestamp = "`'2017-04-20 14:14:00`'"
$dep = "`'17-04-2017`'"
$scenario = "`'A`'"
$milestone = "`'ASB_XREF_GLCC_LOAD_2ND_END_E`'"
$finishtime = "`'2017-04-18/11:11`'"
So for example 2017-04-20 14:14:00 will be '2017-04-20 14:14:00' in the INSERT statement and it will be clear for the server where to separate the values.
Keep in mind that this will handle all the variables as strings (I don't know the data types of your table columns).
Printing out the final SQL query that is being executed I see this -
INSERT INTO APP_PDM_MART.PDM_GEAR_KPI_REP_SNOW (ST_ID, PRIORITY, STATUS, ACTIVE, ACTIVITY_DUE, ADD_REPORTED_BY, BUSINESS_CRITICALITY)
VALUES(0001, CH, 2017-04-20 14:14:00, 17-04-2017, A, ASB_XREF_GLCC_LOAD_2ND_END_E, 2017-04-18/11:11)
You need to correctly insert the date time properties. See this.
I am not able to see the connection string in the query. Where is the $oraConn getting created.
I am considering you do have the db details.
$username = Read-Host -Prompt "Enter database username"
$password = Read-Host -Prompt "Enter database password"
$datasource = Read-Host -Prompt "Enter database TNS name"
$query = "INSERT INTO APP_PDM_MART.PDM_GEAR_KPI_REP_SNOW(ST_ID,PRIORITY,STATUS,ACTIVE,ACTIVITY_DUE,ADD_REPORTED_BY,BUSINESS_CRITICALITY) VALUES($sid,$region,$timestamp,$dep,$scenario,$milestone,$finishtime)"
$connectionString = 'User Id=' + $username + ';Password=' + $password + ';Data Source=' + $datasource
$connection = New-Object Oracle.ManagedDataAccess.Client.OracleConnection($connectionString)
$connection.open()
$command=$connection.CreateCommand()
$command.CommandText=$query
$reader=$command.ExecuteReader()
<#
while ($reader.Read()) {
$reader.GetString(1) + ', ' + $reader.GetString(0)
}
#>
$connection.Close()
You do not have to do the while part, thats why I have commented out.
Refer the blog for further thing: Oracle Database Query From Powershell
If still getting issue, then please post the error as well.
As mentioned in the comment that you do not have access to 64 bit and you wish to only use Oracle.DataAccess.dll, then you can try like this:
[Reflection.Assembly]::Assembly.LoadFrom("c:\Oracle\Oracle11gClientR1\ODP.NET\bin\2.x\Oracle.DataAccess.dll")
OR as mentioned in the comment,to use Add-Type:
Add-Type -AssemblyName "c:\Oracle\Oracle11gClientR1\ODP.NET\bin\2.x\Oracle.DataAccess.dll"
Then I believe you should be able to create the connection string like this:
$con = New-Object Oracle.ManagedDataAccess.Client.OracleConnection(“User Id=username;Password=password;Data Source=localhost/db1”)
$con.open()
Hope it helps.
anyone know how to rewrite below code, so it can invoke sql script. For instance, instead of put 'SELECT ##SERVERNAME AS ServerName' in the powershell script, I would like to put it into sql file.
$Path = "D:\AdminStuff\PowerShell\Password\Password.txt"
$uid = 'sa'
$pwd = Get-Content D:\AdminStuff\PowerShell\Password\Password.txt | ConvertTo-SecureString
$pwd.MakeReadOnly()
$creds = New-Object System.Data.SqlClient.SqlCredential($uid,$pwd)
$con = New-Object System.Data.SqlClient.SqlConnection
$con.ConnectionString = "Server=SLB-CLMFZ52;Database=master;"
$con.Credential = $creds
$con.Open()
$sql = "SELECT ##SERVERNAME AS ServerName"
$cmd = New-Object System.Data.SqlClient.SqlCommand($sql,$con)
$rdr = $cmd.ExecuteReader()
while($rdr.Read())
{
$rdr["ServerName"].ToString()
}
$con.Close()
While you can use the SqlCredential, SqlConnection, and SqlCommand .NET classes as you show in your question, there are simpler alternatives. It is a lot less work, for example, to use the Invoke-Sqlcmd cmdlet instead. Invoke-Sqlcmd is essentially the venerable sqlcmd utility with a PowerShell disguise. Thus, assuming your query was in a file myScript.sql, you can just run this--the InputFile parameter provides the means to store your SQL in a separate file as you requested:
$InvokeParams = #{
Server = 'SLB-CLMFZ52'
Database = 'Master'
Username = 'sa'
Password = 'xyz'
InputFile = 'myScript.sql'
}
Invoke-SqlCmd #InvokeParams
Now the obvious problem with that, though, is the password must be in plain text. (Of course, the password in the OP was in plain text in a file, as well. :-) Unfortunately Invoke-Sqlcmd does not work with PowerShell credential objects, which would have made it a lot more secure. The only reasonable workaround to get some security is to use Windows Authentication instead of SQL authentication. Then you could, for example, omit the username and password, and the query will be invoked with secure credentials:
$InvokeParams = #{
Server = 'SLB-CLMFZ52'
Database = 'Master'
InputFile = 'myScript.sql'
}
Invoke-SqlCmd #InvokeParams
To use the cmdlet just Import-Module sqlps. For the basics of Invoke-SqlCmd take a look at TechNet and for a more in-depth treatment, including the vagaries of sqlps, see part 1 of my Practical PowerShell for SQL Server Developers and DBAs article.
What about something simple like this:
$sql=get-content $filename
Is there a way to execute an arbitrary query on a SQL Server using Powershell on my local machine?
For others who need to do this with just stock .NET and PowerShell (no additional SQL tools installed) here is the function that I use:
function Invoke-SQL {
param(
[string] $dataSource = ".\SQLEXPRESS",
[string] $database = "MasterData",
[string] $sqlCommand = $(throw "Please specify a query.")
)
$connectionString = "Data Source=$dataSource; " +
"Integrated Security=SSPI; " +
"Initial Catalog=$database"
$connection = new-object system.data.SqlClient.SQLConnection($connectionString)
$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
$connection.Open()
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
$connection.Close()
$dataSet.Tables
}
I have been using this so long I don't know who wrote which parts. This was distilled from others' examples, but simplified to be clear and just what is needed without extra dependencies or features.
I use and share this often enough that I have turned this into a script module on GitHub so that you can now go to your modules directory and execute git clone https://github.com/ChrisMagnuson/InvokeSQL and from that point forward invoke-sql will automatically be loaded when you go to use it (assuming your using PowerShell v3 or later).
You can use the Invoke-Sqlcmd cmdlet
Invoke-Sqlcmd -Query "SELECT GETDATE() AS TimeOfQuery;" -ServerInstance "MyComputer\MyInstance"
http://technet.microsoft.com/en-us/library/cc281720.aspx
This function will return the results of a query as an array of powershell objects so you can use them in filters and access columns easily:
function sql($sqlText, $database = "master", $server = ".")
{
$connection = new-object System.Data.SqlClient.SQLConnection("Data Source=$server;Integrated Security=SSPI;Initial Catalog=$database");
$cmd = new-object System.Data.SqlClient.SqlCommand($sqlText, $connection);
$connection.Open();
$reader = $cmd.ExecuteReader()
$results = #()
while ($reader.Read())
{
$row = #{}
for ($i = 0; $i -lt $reader.FieldCount; $i++)
{
$row[$reader.GetName($i)] = $reader.GetValue($i)
}
$results += new-object psobject -property $row
}
$connection.Close();
$results
}
Here's an example I found on this blog.
$cn2 = new-object system.data.SqlClient.SQLConnection("Data Source=machine1;Integrated Security=SSPI;Initial Catalog=master");
$cmd = new-object system.data.sqlclient.sqlcommand("dbcc freeproccache", $cn2);
$cn2.Open();
if ($cmd.ExecuteNonQuery() -ne -1)
{
echo "Failed";
}
$cn2.Close();
Presumably you could substitute a different TSQL statement where it says dbcc freeproccache.
If you want to do it on your local machine instead of in the context of SQL server then I would use the following. It is what we use at my company.
$ServerName = "_ServerName_"
$DatabaseName = "_DatabaseName_"
$Query = "SELECT * FROM Table WHERE Column = ''"
#Timeout parameters
$QueryTimeout = 120
$ConnectionTimeout = 30
#Action of connecting to the Database and executing the query and returning results if there were any.
$conn=New-Object System.Data.SqlClient.SQLConnection
$ConnectionString = "Server={0};Database={1};Integrated Security=True;Connect Timeout={2}" -f $ServerName,$DatabaseName,$ConnectionTimeout
$conn.ConnectionString=$ConnectionString
$conn.Open()
$cmd=New-Object system.Data.SqlClient.SqlCommand($Query,$conn)
$cmd.CommandTimeout=$QueryTimeout
$ds=New-Object system.Data.DataSet
$da=New-Object system.Data.SqlClient.SqlDataAdapter($cmd)
[void]$da.fill($ds)
$conn.Close()
$ds.Tables
Just fill in the $ServerName, $DatabaseName and the $Query variables and you should be good to go.
I am not sure how we originally found this out, but there is something very similar here.
There isn't a built-in "PowerShell" way of running a SQL query. If you have the SQL Server tools installed, you'll get an Invoke-SqlCmd cmdlet.
Because PowerShell is built on .NET, you can use the ADO.NET API to run your queries.
Invoke-Sqlcmd -Query "sp_who" -ServerInstance . -QueryTimeout 3
To avoid SQL Injection with varchar parameters you could use
function sqlExecuteRead($connectionString, $sqlCommand, $pars) {
$connection = new-object system.data.SqlClient.SQLConnection($connectionString)
$connection.Open()
$command = new-object system.data.sqlclient.sqlcommand($sqlCommand, $connection)
if ($pars -and $pars.Keys) {
foreach($key in $pars.keys) {
# avoid injection in varchar parameters
$par = $command.Parameters.Add("#$key", [system.data.SqlDbType]::VarChar, 512);
$par.Value = $pars[$key];
}
}
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataset) | Out-Null
$connection.Close()
return $dataset.tables[0].rows
}
$connectionString = "connectionstringHere"
$sql = "select top 10 Message, TimeStamp, Level from dbo.log " +
"where Message = #MSG and Level like #LEVEL"
$pars = #{
MSG = 'this is a test from powershell'
LEVEL = 'aaa%'
};
sqlExecuteRead $connectionString $sql $pars
You can even format string and pass parameters as you want.
case "ADDSQLSERVERUSER":
//0 = coprorateName;
//1 = user password
//2 = servername
command = #"$sqlQuery = Use JazzUWS_'{0}'
Create login UWSUser_'{0}' with password='{1}';
Create user UWSUser_'{0}' for login UWSUser_'{0}';
Grant Execute to UWSUser_'{0}';
Use ReportSvrUWS_'{0}'
Create user UWSUser_'{0}' for login UWSUser_'{0}';
Grant Execute to UWSUser_'{0}';
Invoke-Sqlcmd -Query $sqlQuery -ServerInstance '{2}'";
break;
C# Code for remote execution(you can organize your way)
string script = PowershellDictionary.GetPowershellCommand("ADDSQLSERVERUSER");
script = String.Format(script, this.CorporateName, password, this.SQLServerName)
PowerShellExecution.RunScriptRemote(_credentials.Server, _credentials.Username, _credentials.Password, new List<string> { script });
You could use the best SQL Server module around: DBATOOLS. You would also benefit from running a query to multiple sql instances.
Install-Module dbatools -Scope CurrentUser
$sql = 'SQL1','SQL1\INSTANCE1','SQL2'
$query = "SELECT 'This query would run on all SQL instances'"
Invoke-DbaQuery -SqlInstance $sqlinstances -Query $query -AppendServerInstance