I'm connecting to SQLServer database using PowerShell. My script running a SQL code works fine but periodically it fails generating desired output and then running it again works fine.
$mySQLQuery = "SQL Query Here"
$myDataSet = New-Object System.Data.DataSet "myDataResultSet"
$sqlConn = New-Object System.Data.SqlClient.SqlConnection("Data Source=myDataSource;Initial Catalog=myDBServer;Integrated Security = False; User ID = UserName; Password =PassWord;")
$SqlConn.ConnectionTimeout= 0
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter($mySQLQuery, $sqlConn)
$adapter.Fill($myDataSet)
$SqlConn.Close()
While, searching and reading I found that it has something to do with connection timeout. I tried to set sql connection timeout to 0 from default 30 but while running the scirpt I'm getting error saying below:
$sqlConn.ConnectionTimeout = 0 is read-only property.
Any idea what I am doing wrong here?
Thank you.
Personally, I set the Command Timeout, rather than the connection...
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = "<yourconnectionstring>"
$command = $connection.CreateCommand()
$command.CommandText = "<yoursql>"
$command.CommandTimeout = 0
$results = $command.ExecuteReader()
$table = New-Object System.Data.DataTable
$table.Load($results)
$connection.Dispose()
the easiest way to fix this is to add ;Connection Timeout=60 to your connect string.
$sqlConn = New-Object System.Data.SqlClient.SqlConnection('Connection Timeout=60')
$sqlConn.ConnectionTimeout
60
you probably don't want it set to 0, use 300 or something if you need a ridiculously high value.
Related
I have an application I made with PowerShell that obtains info from a SQL database for various controls. My 'View', will occasionally work perfectly. But if I close the application and reopen, the same thing can yield different results. I've set PS to run as admin, but again the same issue. The view should read 1971 results and look like
#ASP.Net Client Ugrade
C# Client Upgrade
Another Basic Project Name
However, while this works correctly some of the time...the output I get when it decides to not load correctly is
#ASP.Net Client Ugrade
1971
void Open(), void IDbConne
I'm not sure why it's adding property information instead of the values being requested. Also not sure why the results sometimes are right and sometimes wrong, while doing the same behavior(Just starting the program, the box is populated on load). Relevant code to how I'm selecting and populating the data:
[string[]]$projectsAll = "" #I only do it this way because the sql query is actually in a method, were I return the array-and then do the foreach item in the array add to the projectview
$query = "select Title from OversightProjectsFix where ID > 0 Order By Title"
if ($sqlcon.State -eq 'Closed'){$sqlcon.Open()}
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $query
$SqlCmd.Connection = $sqlcon
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$ds = New-Object System.Data.DataSet
$SqlAdapter.Fill($ds)|out-null
foreach ($Row in $ds.Tables[0].Rows)
{
$projectsAll += "$($Row.Title)"
}
$ProjectView.Items.Clear()
foreach ($p in $projectsAll)
{
$ProjectView.Items.Add($p)
}
As I mentioned before, if this same code is ran, you should always get the same results-but this one returns differently and I'm not sure why? How can I make it always work?
It's hard to understand exactly what is wrong with this snippit of code, but I think that it may be because the DataSet is empty, and you are . i.e. I think that it isn't connecting and pulling any data.
Since the dataset is initialized with New-Object, if it didn't fill properly, what happens is if you try to iterating through it using quotes, instead of adding it as a straight assignment, it may cause it to unexpectedly return the data type instead of an "expected" null or empty string. You can see this when you try something like this:
$ds = New-Object System.Data.DataSet
#Returns Null
PS> $ds.Tables[0].Rows
PS>
#Returns data type
PS> "$ds.Tables[0].Rows"
System.Data.DataSet.Tables[0].Rows
So you may have to remove the quotes i.e.:
$projectsAll += $Row.Title
You should add a check to see if the dataset has something before trying to iterate and add anything.
Also, usually by implementing the $SqlAdapter.Fill($ds) method opens the connection, without you needing to explicitly open the SQL connection. I wonder if your inconsistencies is due to the SQL connection opening/closing unexpectedly?
I'm trying to query a SQL database and return only the messages that are produced, table results aren't important. I've looked at several answers online and came up with the following code, but every time I output the $message it is empty.
Can someone tell me what I'm doing wrong here or if there's some hidden problem?
$server = 'SampleServer'
$sqlQuery = "print 'sample message';"
$sqlConnection = new-object System.Data.SqlClient.SqlConnection("Server=$server; Trusted_Connection=True;")
$sqlConnection.FireInfoMessageEventOnUserErrors = $true
#Listener for messages
$handler = [System.Data.SqlClient.SqlInfoMessageEventHandler]{$message = "$($_)"}
$sqlConnection.add_InfoMessage($handler)
#tried using events, but it also didn't return anything. Assumed $Event was some system variable?
# Register-ObjectEvent -InputObject $sqlConnection -EventName InfoMessage -Action {$message = "$($Event.SourceEventArgs)"} -SupportEvent
#run Query
$sqlConnection.Open()
$sqlCommand = $sqlConnection.CreateCommand()
$sqlCommand.CommandText = $SQLQuery
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter $sqlcommand
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | out-null
$sqlConnection.Close()
#output $message results
write-host $message
Also, I can't use Invoke-SqlCommand.
EDIT So, after tinkering with the code some more, there's not really a problem with it, but there's a problem with how the query message is being returned from the $handler block. If I put a write-host in the $handler block it works just fine, but I need the information later in my script. I've tried these solutions to no avail (also added return $message to $handler):
$message = $sqlConnection.add_InfoMessage($handler) #nothing returned
$sqlConnection.add_InfoMessage($message = $handler) #just plain wrong
Is there a correct location to put an assignment in this situation?
I've searched all over the web and am unable to find a solution/guide for my problem.
I'm using the below bit of script its part of a larger script to export multiple SQL tables into CSVs. It fills a dataset with data from an SQL table.
The problem I have is in mainly in relation to datetime settings. For example, If I were to export the SQL table using the export wizard into a CSV file. The date appears exactly like it does in SQL e.g. "2014-05-23 07:00:00.0000000" or "2014-05-23".
However when I use my script it changes the format of the datetime to "23/05/2014 07:00:00" or "23/05/2014 00:00:00". I believe this has something to do with the culture settings of my machine/powershell session.
cls
# Declare variables for connection and table to export
$Server = 'server'
$Database = 'database'
$Folder = 'D:\Powershell Scripts\01_Export From SQL\Test Folder'
$Tablename1 = 'test'
$Tablename2 = ''
# Delcare Connection Variables
$SQLconnection = New-Object System.Data.SqlClient.SqlConnection
$SQLconnection.ConnectionString = "Integrated Security=SSPI;server=$Server;Database=$Database"
# Delcare SQL command variables
$SQLcommand = New-Object System.Data.SqlClient.SqlCommand
$SQLcommand.CommandText = "SELECT [name] from sys.tables where [name] like '%$Tablename1%' and [name] like '%$Tablename2%' order by [name]"
$SQLcommand.Connection = $SQLconnection
# Load up the Tables in a dataset
$SQLAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SQLAdapter.SelectCommand = $SQLcommand
$DataSet = New-Object System.Data.DataSet
$null = $SqlAdapter.Fill($DataSet)
$SQLconnection.Close()
"Time to Export`tRecords `tFile Name"
"--------------`t------- `t---------"
foreach ($Table in $DataSet.Tables[0])
{
$stopwatch = [system.diagnostics.stopwatch]::StartNew()
$FileExtractUTF8 = "$Folder\FromPSUTF8_$($Table[0]).csv"
$SQLcommand.CommandText = "SELECT * FROM [$($Table[0])]"
$SQLAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SQLAdapter.SelectCommand = $SQLcommand
$DataSet = New-Object System.Data.DataSet
$Count = $SQLAdapter.Fill($DataSet)
$SQLconnection.Close()
$DataSet.Tables[0] | Export-Csv $FileExtractUTF8 -NoTypeInformation -Encoding UTF8
$stopwatch.stop()
$Time = "{0}" -f $stopwatch.Elapsed.ToString('mm\:ss\.fff')
“{0,-14}`t{1,-10}`t{2}” -f $Time,$Count,$Table.name
}
The main goal is to export the data from SQL into a flat file with the data appearing exactly as it would if I used the export wizard.
Changing default DateTime format within a script
Within your DataSet / DataTable, the date exists as a [DateTime] type. When you export to CSV it needs to be converted to a string so it can be written to file. As you have observed, the default string conversion gives an output such as:
[PS]> (get-date).ToString()
21/03/2017 17:52:26
The format of this string is specified (as you worked out) by your specific globalization "culture". It appears to be a concatenation of the ShortDatePattern and LongTimePattern properties of the DateTimeFormat.
There are plenty of posts from people who have tried and failed to change the culture of their current session (i.e. the running PS host)...
why-does-powershell-always-use-us-culture-when-casting-to-datetime
...but it may be possible to change the globalization culture within your script using a mechanism such as the ones described here:
powershell-changing-the-culture-of-current-session
Using-Culture function
I suspect you should be able to use the Using-Culture example to wrap the Export-Csv line in your script.
But what culture to use?
So, you might now be able to set a specific culture, but do any of the pre-existing cultures use ISO8601 (sortable) dates? Or more specific formats? It seems not, so you have to make your own!
In short, you need to clone an existing CultureInfo and update the DateTimeFormat, specifically (at least) the ShortDatePattern to be what you want. Here is an example that I hope puts you on the right path. There are two functions, one which clones an existing CultureInfo and one which runs a command (Get-Date) with the new culture set for that thread.
function New-CultureInfo {
[CmdletBinding()]
[OutputType([System.Globalization.CultureInfo])]
param(
[Parameter()]
[System.Globalization.CultureInfo]
$BaseCulture = [System.Globalization.CultureInfo]::InvariantCulture
)
$CultureInfo = ($BaseCulture).Clone()
$CultureInfo.DateTimeFormat.ShortDatePattern = "yyyy'-'MM'-'dd"
$CultureInfo.DateTimeFormat.LongTimePattern = "HH:mm:ss.fff"
return $CultureInfo
}
function Test-DateFormat {
[CmdletBinding()]
[OutputType([System.DateTime])]
param(
[Parameter(Mandatory)]
[System.Globalization.CultureInfo]
$Culture
)
[System.Threading.Thread]::CurrentThread.CurrentUICulture = $Culture
[System.Threading.Thread]::CurrentThread.CurrentCulture = $Culture
(Get-Date).ToString()
}
Example use:
[PS]> $NewCulture = (New-CultureInfo)
[PS]> Test-DateFormat $NewCulture
2017-03-21 19:08:34.691
Now, I haven't been able to run this against an example close to the SQL problem in the OP, but I've had fun working this all out. ;-)
Great description by Charlie. My problem is that I wanted to change the default ToString to output an ISO datetime that contains a T separator between date and time instead of a space. TL;DR - it's not possible.
I'm more from the Java world than MS but had to write a script to export db CSVs and here's my investigation in case anyone else is interested in how the format is built. I dug into the source code to see how ToString works on DateTime.
According to the DateTime class it will defer to DateTimeFormat
https://referencesource.microsoft.com/#mscorlib/system/datetime.cs,0a4888bea7300518
public override String ToString() {
Contract.Ensures(Contract.Result<String>() != null);
return DateTimeFormat.Format(this, null, DateTimeFormatInfo.CurrentInfo);
}
This will eventually call into Format method with a null String format. This causes the block below to be called which basically specifies the "G" format to use for the date/time.
https://referencesource.microsoft.com/#mscorlib/system/globalization/datetimeformat.cs,386784dd90f395bd
if (offset == NullOffset) {
// Default DateTime.ToString case.
if (timeOnlySpecialCase) {
format = "s";
}
else {
format = "G";
}
}
This will eventually make a call to get the current Culture's DateTimeInfo object that has an internal String pattern that cannot be overridden. It lazily sets this so that it doesn't have to concatenate and there is no way to override it. As Charlie pointed out, it always concatenates ShortDatePattern and LongTimePattern with a space separator.
https://referencesource.microsoft.com/#mscorlib/system/globalization/datetimeformatinfo.cs,799bd6e7997c4e78
internal String GeneralLongTimePattern {
get {
if (generalLongTimePattern == null) {
generalLongTimePattern = ShortDatePattern + " " + LongTimePattern;
}
return (generalLongTimePattern);
}
}
So there you have it. Hope it helps the next person know why/how and that it's not possible to override the overall default format - just the date and the time components.
I want to generate an import script for a MSSQL DB via Powershell (related to this question).
I tried doing this:
#Set-ExecutionPolicy RemoteSigned
$DB_NAME = "<<dbName>>"
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
$srv = new-object "Microsoft.SqlServer.Management.SMO.Server" "<<server>>"
$conContext = $srv.ConnectionContext
$conContext.LoginSecure = $false
$conContext.Login = "<<user>>"
$conContext.Password = "<<password>>"
$srv = new-object Microsoft.SqlServer.Management.Smo.Server($conContext)
$srv.SetDefaultInitFields([Microsoft.SqlServer.Management.SMO.View], "IsSystemObject")
$db = $srv.databases[$DB_NAME]
$scripter = new-object "Microsoft.SqlServer.Management.Smo.Scripter" $srv
$scripter.Options.ScriptSchema = $false
$scripter.Options.ScriptData = $true
$scripter.Options.ScriptDrops = $false
$scripter.Script($db)
But executing this throws an error:
"This method does not support scripting data"
I also tried to set the output file option but this doesn't change anything.
Can you tell me what I did wrong?
Thanks!
Per the error, Scripter.Script does not support scripting data. This is documented. What isn't documented is what you're supposed to use instead, but it's EnumScript:
$scripter.EnumScript(#($db.Tables))
You must pass the tables, since simply scripting the database will yield nothing (as, technically, the database itself contains no data, its tables do).
(The #() forcibly converts the Tables collection to an array, since that's what EnumScript expects.)
I have an interesting issue here. I'm creating a calendar picker for use when we create accounts. It works fine and is still in progress but I have noticed that when I run the script in powershell ISE, after a few minutes it locks up (I am able to edit and save the code for a few minutes prior to that). There is nothing in the event log. I get a dialog box saying that powershell is non responsive. Memory usage seems normal as well. I do not know what is happening.
This occurs no matter how I run Powershell ISE (Run as Administrator, Run as another account, and normal ISE) I am running windows 8.1.
A coworker suggested it may be the apartment model, so I've tried STA and MTA, but the problem occurs either way. It does not happen when the same code is run from the console host.
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$objForm = New-Object Windows.Forms.Form
$objForm.Text = "Select a Date"
$objForm.Size = New-Object Drawing.Size #(490,250)
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True
$objForm.Add_KeyDown({
if ($_.KeyCode -eq "Enter")
{
$script:dtmDate=$objCalendar.SelectionStart
$objForm.Close()
}
})
$objForm.Add_KeyDown({
if ($_.KeyCode -eq "Escape")
{
$objForm.Close()
}
})
$objCalendar = New-Object System.Windows.Forms.MonthCalendar
$objCalendar.Text = "Start"
$objCalendar.ShowTodayCircle = $False
$objCalendar.MaxSelectionCount = 1
$objForm.Controls.Add($objCalendar)
$objForm.Topmost = $True
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
if ($dtmDate)
{
Write-Host "Date selected: $dtmDate"
}
$objForm.Dispose()
In Response to #The Unique Paul Smith
function Find-CalenderDateTest {
[CmdletBinding()]
param(
[Parameter(
Mandatory=$false
)]
[ValidateSet('long','short','powerpoint')]
[ValidateNotNullOrEmpty()]
[string]
$DateFormat
)
Begin{
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$objForm = New-Object Windows.Forms.Form
$objForm.Text = "Select a Date"
$objForm.Size = New-Object Drawing.Size #(243,250)
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True
$dtmDate = $null
$objForm.Add_KeyDown( {
if ($_.KeyCode -eq "Enter")
{
$dtmDate=$objCalendar.SelectionStart
$objForm.Close()
}
})
$objForm.Add_KeyDown({
if ($_.KeyCode -eq "Escape")
{
$objForm.Close()
}
})
#region OK Button
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(20,175)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
# Got rid of the Click event for OK Button, and instead just assigned its DialogResult property to OK.
$OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$objForm.Controls.Add($OKButton)
# Setting the form's AcceptButton property causes it to automatically intercept the Enter keystroke and
# treat it as clicking the OK button (without having to write your own KeyDown events).
$objForm.AcceptButton = $OKButton
#endregion
#region Cancel Button
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(80,175)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
# Got rid of the Click event for Cancel Button, and instead just assigned its DialogResult property to Cancel.
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$objForm.Controls.Add($CancelButton)
# Setting the form's CancelButton property causes it to automatically intercept the Escape keystroke and
# treat it as clicking the OK button (without having to write your own KeyDown events).
$objForm.CancelButton = $CancelButton
#endregion
$objCalendar = New-Object System.Windows.Forms.MonthCalendar
$objCalendar.ShowTodayCircle = $False
$objCalendar.MaxSelectionCount = 1
$objForm.Controls.Add($objCalendar)
$objForm.Topmost = $True
$objForm.Add_Shown({$objForm.Activate()})
$Results = $objForm.ShowDialog()
}
Process{}
End{
if ($Results -eq "OK")
{
$objCalendar.SelectionStart
}
$objForm.Dispose()
}
}
The error is MTA/STA
Don't use
$form.showDialog()
Use
[system.windows.forms.application]::run($form)
instead
and it works fine every time
Another way is to put it in another thread:
$code
{
//form code here
$form.showDialog()
}
$newThread = [Powershell]::Create()
$newThread.AddScript($code)
$handle = $newThread.BeginInvoke()
Provide variables from the calling script:
$newThread.Runspace.SessionStateProxy.SetVariable("variablenname",value)
before the BeginInvoke use variablenname without $...
It's a long shot but the problem might be that powershell is not closing the $objForm object correctly, leaving it running in memory while the ISE waits for input after the script has terminated. If you check your taskmanager, is the form still running in the background? You could also try adding 'Remove-Variable objForm' (no $) after the dispose() and see if that helps.
More information: https://technet.microsoft.com/en-us/library/ff730962.aspx
As I say, it's a long shot.
I was using combobox.items.add:
$configCombo.Items.Add($wks)
and I looked up how to keep the keys from printing to the console - and changed the add to:
[void]$configCombo.Items.Add($wks)
Since then I have added the void - I have been running it in ISE and it has not hung since.
Ran into this issue too. Generally occurs when I lock my workstation and return. After a bit of poking about and googleing, I found this
https://support.microsoft.com/en-us/help/943139/windows-forms-application-freezes-when-system-settings-are-changed-or, which seems like the issue at hand.
Issue
The application will not respond and the UI thread will hang in an
Invoke call while handling the OnUserPreferenceChanged notification
Cause
This occurs if a control is created on a thread which doesn't pump
messages and the UI thread receives a WM_SETTINGCHANGE message.
Resolution
Applications should never leave Control objects on threads without an
active message pump. If Controls cannot be created on the main UI
thread, they should be created on a dedicated secondary UI thread and
Disposed as soon as they are no longer needed.
I had the same issue, but the solution is: Always clean up right after the form is done.
In this case:
$objForm.Dispose()
Up to now (a few hours) I didn't have that issue again. Previously it locked up after > 10 Minutes.