How To Prevent Invoke-SQLCMD from trimming Leading Zero's - powershell

In powershell I have a script and every time it runs it needs to increment a number in a DB by one. Once it reaches 9999 it resets back to 0001.
I have this functionality worked out in powershell my issue is that Invoke-SQLCMD
keeps stripping out any leading 0's
so If I want to update the DB value to 0001 it only updates it to 1
Is there any way to have SQLCMD keep leading 0's?
Invoke-Sqlcmd -ServerInstance $dataSource -Database $database -Username $user -password $pass -Query "UPDATE DBO.TABLE_NAME SET sequence_no = $newFileNum"

invoke SQLCMD does this by default.
One way is when you run your query against SQL to get the value in there you can use .ToString("0000") to put the 0's back in or your can use this as your SQL query.

Related

PowerShell while loop when function returns value

I have several steps in powershell invoked against sql server.
backup database
add database to availability group
turn on NAV middle tier
Problem is, that add to availability group step just executes command and does not wait until database is recovered on secondary nod. I have a function to check state of database on secondary and this function should wait until database is online and then start NAV middle tier.
I get correct database state (ONLINE/RECOVERING...), ,but somehow I cannot figure out WHILE part. My example here runs even if database is ONLINE or OFFLINE.
What Am doing wrong?
function get-dbstate($db_name_restore){
$query_check_state = "select state_desc from sys.databases where [name] = '"+ $db_name_restore +"'"
$state = invoke-sqlcmd -ServerInstance server2 -Query $query_check_state -ErrorAction Stop
return $state.state_desc
}
while(get-dbstate($db_name_restore) -ne "ONLINE"){
write-host("...still restoring")
Start-Sleep -Seconds 2
}
Ok correct syntax is to put function into brackets:
while((get-dbstate $db_name_restore) -ne "ONLINE"){
write-host "...still restoring"
Start-Sleep -Seconds 2
}

Why does write-host output to transcript but write-information does not?

This one is confusing me and wondered if anyone could shed some light on this, maybe also answer 2 parts with this one example?
I have a Powershell script I run like a batch file and I have it output values it has captured from a separate file out to a log. This particular part is not outputting in the transcript where I get the DB version of a database. I have tried different placements of the ", using $DBVersion on it's own and this is a simple way to show what I have trouble with. e.g.:
## Read the DBs Extended properties
Function Get-DB_Version {
Param (
$SQLInstance,
$DBName
)
Invoke-Sqlcmd -ServerInstance $SQLInstance -Database $DBName -Query "SELECT value FROM fn_listextendedproperty(default, default, default, default, default, default, default) WHERE name = 'version'"
}
**Other Variables used are also pre-set above here **
## Get DB version
$DBVersion = Get-DB_Version -SQLInstance $SQLInstance -DBName $DBName
Start-Transcript -Path "$LogOutput\$LogName" -IncludeInvocationHeader -Append
Write-Information "
**** DEBUG INFO ****
Write-Host "Debug:"$DBVersion.value
Write-Information "Debug:"$DBVersion.value
Read-Host -Prompt "PAUSE" # This is so I can visually see the console seeing only the write-host is there as expected.
Stop-Transcript
In my log file I get the output:
Debug: 2.16.51443.5147
INFO: Debug:
This shows me that the variable contains a value as the write-host outputs it, however when use Write-Information it does not show anything in the log, All other variables I use do show, why would $DBVersion.value or $DBVersion not show anything please?
Also the second part is, why do I have to use:
$DBVersion.value
Outside of the write-host "" quotes?
Many thank in Advance
As #abraham said in the comments. All I had to do, to have the variable inside of the quotes (my question 2) was use the sub-expression operator $() to expand the value inside the quotes: Write-Host "Debug: $($DBVersion.value)". The same goes for your Write-Information.
Doing this alone also resolved my original question of why Write-Information didn't output anything into the transaction logs and I did NOT need to change the $InformationPreference.

Invoke-Sqlcmd cannot find file when BULK INSERT is used

I'm having difficulty invoking the following PowerShell command, from the command line as well as in a script:
Invoke-Sqlcmd -HostName **HOST** -Database **DATABASE**
-Username **USER** -Password **PWD** -Query "TRUNCATE **THE_TABLE**
BULK INSERT **THE_TABLE** FROM 'D:\Folder\csvfiles\import.csv'
WITH ( FIRSTROW = 2, FIELDTERMINATOR = ',', ROWTERMINATOR = '\n',TABLOCK)"
-ServerInstance "tcp:**HOST**"
I've copied the CSV file to the parent folder, and then to the root. Each time, the command fails with the following error:
Invoke-Sqlcmd : Cannot bulk load. The file "D:\Folder\csvfiles\import.csv" does not exist.
At line:1 char:1
This is all part of a script that Task Scheduler runs on an hourly basis. Often it will run just fine. Today it has been erroring with more frequency.
I'm stumped. Why can't it find a file that is obviously there? Am I overlooking something?
I found out that there was a related, competing task scheduled that was deleting all of the csv files in the folder at very close to the same time that my script ran. This turned out to be a case of bad timing.

Run SQL script file from powershell

I am trying to run queries stored in a text file from PowerShell. I use following to do that;
Invoke-Expression 'sqlcmd -d TestDB -U $user -P $pw -i "E:\SQLQuery1.sql"'
If an error or exception occurs when executing the queries from the .sql file, how can I capture that in my Powershell script? How can I get the script output?
NOTE: I cannot use invoke-sqlcmd
To answer the question
If some error or exception occurred when executing .sql file how can I get that into my PowerShell script? How can I get the script output?"
Invoke-Expression returns the output of the expression executed. However, it may only capture STDOUT, not STDERR (I haven't tested, as I don't use this method), so you might not get the actual error message.
From the Help:
The Invoke-Expression cmdlet evaluates or runs a specified string as a command and returns the results of the expression or command
The better route is to use the PowerShell method you already have available - Invoke-SQLCmd is installed if you have installed any of the SQL Server 2008 (or newer) components/tools (like SSMS). If you've got SQL Server 2012, it's very easy: import-module sqlps. For 2008, you need to add a Snap-In, add-pssnapin SqlServerCmdletSnapin. And since you have sqlcmd.exe, the PowerShell components should be there already.
If all else fails, go the System.Data.SQLClient route:
$Conn=New-Object System.Data.SQLClient.SQLConnection "Server=YOURSERVER;Database=TestDB;User Id=$user;password=$pw";
$Conn.Open();
$DataCmd = New-Object System.Data.SqlClient.SqlCommand;
$MyQuery = get-content "e:\SQLQuery1.sql";
$DataCmd.CommandText = $MyQuery;
$DataCmd.Connection = $Conn;
$DAadapter = New-Object System.Data.SqlClient.SqlDataAdapter;
$DAadapter.SelectCommand = $DataCmd;
$DTable = New-Object System.Data.DataTable;
$DAadapter.Fill($DTable)|Out-Null;
$Conn.Close();
$Conn.Dispose();
$DTable;
With both this and Invoke-SQLCmd, you'll be able to use try/catch to pick up and handle any error that occurs.
As seen in this question's answers, there is a method built into Powershell to invoke SQLCMD called, unsurprisingly, Invoke-Sqlcmd.
It's very easy to use for individual files:
Invoke-sqlcmd -ServerInstance $server -Database $db -InputFile $filename
Or groups:
$listOfFiles | % { Invoke-sqlcmd -ServerInstance $server -Database $db -InputFile $_ }
Use invoke-sqlquery module, available at this website.

Powershell Invoke-sqlcmd keeping connection open and trying to reuse it

I have a Powershell script that uses invoke-sqlcmd to apply scripts to a series of development databases. I loop through a list of scripts and compare it to the current release level of the database and then apply the required scripts to get the DB to the release level it needs to be at. Certain databases are reference databases and are in a READ_ONLY state. I connect to those database run an alter DB script setting them to READ_WRITE apply the script then change the back to READ_ONLY. Overall the script works well, the issue is it looks like when PowerShell first opens a connection to the database and applies the first script and then goes to alter the DB back to READ_ONLY the database has objects locked. I've traced it back to the previous connection and a Shared_Transaction_Workspace lock (sys.dm_tran_locks) for what looks to be the previous powershell connection. Why is this connection still open after the invoke-sqlcmd has completed and is there anything I can do about it? Can I force invoke-sqlcmd to use a new connection for each invocation of the cmdlet?
I have tried a messy fix killing the offending connection and then retrying the connection but I think there is something better.
I've always done this and it seems to work:
[System.Data.SqlClient.SqlConnection]::ClearAllPools()
Well, I know that this is a very old post and the people from Microsoft told that fixed this issue (as told the article mentioned by David Brabant) but maybe I'm not the luckiest guy and have to make an workaround to make it happens.
Even running Microsoft SQL Server 2012 (SP1) - 11.0.3128.0 (X64) I had the same issue and after make some researches I got a way to get some parameter from Invoke-Sqlcmd as output so I can get the Session ID of the current user process with the built-in ##SPID global variable from the SQL Server and make a connection with ADO.NET to execute a KILL clause to close the opened connection.
So let's to the workaround applied in my case
#Invoke the Invoke-Sqlcmd to execute an script from a file
Invoke-Sqlcmd -Server "[SERVER_NAME]" -Database [DATABASE_NAME] -Username [USER] -Password [PASSWORD] -InputFile [DOT_SQL_FILE_PATH]
#Invoke the Invoke-Sqlcmd to execute a inline SQL statement to get the SessionID as a Powershell variable
$SQLSession = Invoke-Sqlcmd -Server "[SERVER_NAME]" -Database [DATABASE_NAME] -Username [USER] -Password [PASSWORD] -query "select ##spid as SessionID"
# Build query to execute KILL clause
$DbQuery = "KILL " + $SQLSession.SessionID;
# Create SQL connection with ADO.NET
$DbConnection = New-Object System.Data.SqlClient.SqlConnection
$DbConnectionString = "Server = [SERVER_NAME]; Database = [DATABASE_NAME]; User ID=[USER]; Password=[PASSWORD];"
$DbConnection.ConnectionString = $DbConnectionString
$DbConnection.Open()
# Create SQL command for KILL clause
$DbCommand = New-Object System.Data.SQLClient.SQLCommand
$DbCommand.Connection = $DbConnection
$DbCommand.CommandText = $DbQuery
# Execute KILL clause
$DbCommand.ExecuteNonQuery()
# Close connection
$DbConnection.Close()
I hope that it helps
Even though I am using the newest version of SSMS (Version 16.5.3 - Build 13.0.16106.4), I still get this issue. I haven't figured out what the "right" way of forcing the connection closed is, but I have a work-around that is simple and resolves the issue for me. If you just need to get the connection off the database, you can do the following:
Run normal command(s)
Invoke-Sqlcmd -ServerInstance "SOME_SERVER" -Database "SOME_DB" ...
When you are ready to eliminate the connection from the database:
Invoke-Sqlcmd -ServerInstance "SOME_SERVER" -Database "SOME_DB" -Query "use [master];"
This will switch the connection to master, thus removing it from the database of interest. If you absolutely need the connection closed, I think you need to resort to SqlClient or such.