continue after exec command in t-sql - tsql

With-in t-sql I want to execute one proceduere and go for next excuatable statment immidiatly i.e. do not wait to complete that particular command line.
e.g.
Statment1
Statment2
exec dbtemp..CustomeProc1
Statement3
In above example after start processing of
exec dbtemp..CustomeProc1
programe should not wait to complete the action and go for
Statement3

T-SQL isn’t going to work because these needs to run asynchronously and not synchronously (all at the same time not one after the other) and T-SQL doesn’t have that option.
Using Powershell you can achieve it -
$Server= "(local)\sql2016cs" ;
$DBName = "Test" ;
$Commands = #()
$Commands += #()
$Commands += "EXEC dbtemp..CustomeProc1"
$Commands += #()
foreach ($sql in $Commands ) {
$cmdstr =#"`Add-Type -AssemblyName"Microsoft.SqlServer.Smo,Version=$(13).0.0.0,Culture=neutral,PublicKeyToken=89845dcd8080cc91"
`[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
`$SqlConn = New-Object Microsoft.SqlServer.Management.Smo.Server ("$Server")
`$SqlConn.Databases["$DBName"].ExecuteNonQuery("$sql")
"#
$cmd = [ScriptBlock]::Create($cmdstr)
Start-Job -ScriptBlock $cmd
}

Related

Powershell: system-diagnostics-processstartinfo hangs, reason unknown

Highly influenced by other questions here on Stackoverflow I have ended up with this method for starting processes from my Powershell-scripts
function global:system-diagnostics-processstartinfo {
[CmdletBinding(SupportsShouldProcess=$True,ConfirmImpact='Low')]
param
(
[Parameter(Mandatory=$True,HelpMessage='Full path to exectuable')]
[Alias('exectuable')]
[string]$exe,
[Parameter(Mandatory=$True,HelpMessage='All arguments to be sent to exectuable')]
[Alias('args')]
[string]$arguments
)
if (!(Test-Path $exe)) {
$log.errorFormat("Did not find exectuable={0}, aborting script", $exe)
exit 1
}
$log.infoFormat("Start exectuable={0} with arguments='{1}'", $exe, $arguments)
$processStartInfo = New-Object System.Diagnostics.ProcessStartInfo($exe)
$processStartInfo.FileName = $exe
$processStartInfo.RedirectStandardError = $true
$processStartInfo.RedirectStandardOutput = $true
$processStartInfo.UseShellExecute = $false
$processStartInfo.Arguments = $arguments
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $processStartInfo
$log.info("Start exectuable and wait for exit")
$p.Start() | Out-Null
#$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
$log.infoFormat("exectuable={0} stdout: {1}", $exe, $stdout)
$log.debugFormat("exectuable={0} stderr: {1}", $exe,$stderr)
$global:ExitCode = $p.ExitCode
$log.debugFormat("exectuable={0} Exitcode: {1}", $exe, $p.ExitCode)
return $stdout
}
Pretty straight forward with some added logging etc. And it works in all my current use cases execpt one. I have created a script that copies the database dump for our production instance of Confluence to our test server. Then it uses the above method to drop existing database, all fine. But the actual restore just hangs for ever and ever. So right now I have to exit the script and then run the following command manually
d:\postgresql\bin\pg_restore.exe -U postgres -d confluencedb -v -1 d:\temp\latest-backup.pgdump
It takes some time and there is quite a lot of output. Which makes me belive that there must be either one the following causing the issue
The amount of output makes a buffer overflow and stalls the script
It takes to much time
Anyone with similar experiences who can help me resolve this. It would enable to schedule the import, not having to do it manually as today.
I had to do the following right after process. Start:
# Capture output during process execution so we don't hang
# if there is too much output.
do
{
if (!$process.StandardOutput.EndOfStream)
{
[void]$StdOut.AppendLine($process.StandardOutput.ReadLine())
}
if (!$process.StandardError.EndOfStream)
{
[void]$StdErr.AppendLine($process.StandardError.ReadLine())
}
Start-Sleep -Milliseconds 10
}
while (!$process.HasExited)
# Capture any standard output generated between our last poll and process end.
while (!$process.StandardOutput.EndOfStream)
{
[void]$StdOut.AppendLine($process.StandardOutput.ReadLine())
}
# Capture any error output generated between our last poll and process end.
while (!$process.StandardError.EndOfStream)
{
[void]$StdErr.AppendLine($process.StandardError.ReadLine())
}
# Wait for the process to exit.
$process.WaitForExit()
LogWriteFunc ("END process: " + $ProcessName)
if ($process.ExitCode -ne 0)
{
LogWriteFunc ("Error: Script execution failed: " + $process.ExitCode )
$FuncResult = 1
}
# Log and display any standard output.
if ($StdOut.Length -gt 0)
{
LogWriteFunc ($StdOut.ToString())
}
# Log and display any error output.
if ($StdErr.Length -gt 0)
{
LogWriteFunc ($StdErr.ToString())
}

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 get a loop for telnet port check in 1 line for Powershell?

I would like to streamline the following command into 1 line, to do a loop until it returns a TRUE.
The following will check if the TCP connection is working correctly:
(New-Object System.Net.Sockets.TcpClient -ArgumentList DESTINATION-IP,PORT).Connected
This will normally print/return a TRUE if there is no blocking of any firewall.
What I want to do is, without a script/file, to have this command being looped until it returns a TRUE value.
Any ideas?
Something like this would work:
while ((New-Object System.Net.Sockets.TcpClient -ArgumentList DESTINATION-IP,PORT).Connected -ne $true) { Start-Sleep -Milliseconds 1000 }
I would not recommend sleeping less than ~250 milliseconds or so unless you actually want to connection flood.
Also, remember that you don't need to make a one-liner to call a script without a script file. Powershell has an end of line terminator: ;. You can easily call powershell -Command { 1; 2; 3; }.
Try this to be a bit more robust. You can remove the whitespace and line breaks to make it into a single line, but I'll post it formatted for legibility:
do {
try {
$Connected = (New-Object System.Net.Sockets.TcpClient -ArgumentList DESTINATION-IP,PORT -ErrorAction Stop).Connected;
}
catch {
$Connected = $false;
}
if (!$Connected) { Start-Sleep -Milliseconds 1000; }
} until ($true.Equals($Connected));

Start() Error In Powershell (Starting a Shortcut)

I have created a Powershell script to do a timed restart on my server. This is a snippet of my code and more specifically the startServers Function.
I have added the global variables into the function in an effort to have an example.
Basically each server name is the name of a shortcut that starts an exe in a separate directory. The names are exactly that and have no .lnk after (I have tried both ways).
The aff array is for processor affinity.
This script should:
For Each Server
Create a New Sysem Diag Process with start info
Start the process
Set affinity
This is not the case: I get an error revolving around the "arg0" and it not being able to start the process. I suppose if anyone has some further explanation of what is going on with args0 and what it is would be helpful.
Code Snippet:
function startServers
{
#Added For Example Purposes
$servers = #( "public", "private" )
$aff = #( 196, 56 )
#End
$i = 0
foreach($s in $servers)
{
$app_name = "$s"
$a = $aff[$i]
$app_arguments = "arg0"
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.Arguments = $app_arguments
$pinfo.FileName = $app_name
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start()
$p.ProcessorAffinity=$a
$i = $i + 1
#Start "$s"
Write-Output "Starting $s"
}
}
function startServers {
$i = 0
$global:pids = #( )
foreach($s in $servers)
{
$a = $aff[$i]
$Starter = Start-Process -FilePath $s -WindowStyle Minimized -PassThru
$Process = Get-Process -Id $Starter.ID
$global:pids += $Process.ID
$Process.ProcessorAffinity = $a
$CurrentTime = updateTime
Write-Output "Starting $s at $CurrentTime"
$i = $i + 1
Start-Sleep -s 10
}
return
}
This is how I ended up solving the problem. I switched over to tracking the process by PID.
I used -PassThru on my Start-Process and I made it a variable.
I then made a variable that would hold the PID after it was started called Process.
Then you do the basic $foo.ProcessorAffinity = foo and you get your result.

PowerShell script that runs in background changing specific process priority to low whenever it is ran

I am trying to deploy a script via our Group Policy that will run in the background and watch for a process called "3dsmax.exe". Each time this process is started, I want to make sure it's priority level is set to "low".
My Powershell understanding is extremely limited.
I tried combining the following using different forums but that does not seem to work and I beleive this will terminate after first occurrence which is not preferable. I use "calc.exe" as a testing process.
#requires -version 2.0
Register-WmiEvent -Class win32_ProcessStartTrace -SourceIdentifier processStarted
$prog = 'calc.exe'
$newEvent = Wait-Event -SourceIdentifier processStarted
If ($progs -match $newEvent.SourceEventArgs.NewEvent.ProcessName)
{
$prog = Get-Process -Name calc
$prog.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::Low
}
Any help would be super:)
Will this be a good way to go about this?
It seems to work, but I am not sure this is a good way of entering an endless loop with the aforementioned concerns.
while($true)
{
$Query = "select * from __instanceCreationEvent within 1 where targetInstance isa 'win32_Process' AND TargetInstance.Name = 'calc.exe'"
$Eventwatcher = New-Object management.managementEventWatcher $Query
$Event = $Eventwatcher.waitForNextEvent()
$prog = Get-Process -Name calc
$prog.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::Idle
}
Here is an alternative method. The "::Low", was not recognized when I tested, had to use ::Idle. EDIT: added foreach ? | loop to suppress error in case of multiple instances. Added Idle loop to begin of script to catch any pre-existing processes upon open.
$prog = Get-Process -Name calc | ?{$_.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::Idle}
while($true)
{
$Query = "select * from __instanceCreationEvent within 1 where targetInstance isa 'win32_Process' AND TargetInstance.Name = 'calc.exe'"
$Eventwatcher = New-Object management.managementEventWatcher $Query
$Event = $Eventwatcher.waitForNextEvent()
$prog = Get-Process -Name calc | ?{$_.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::Idle}
}
And here is a little .vbs snip to open the app invisibly, so your users don't get a powershell window.
command = "powershell.exe -nologo -command C:\IDLER.ps1"
set shell = CreateObject("WScript.Shell")
shell.Run command,0