I experienced a very strange issue and can be repeated.
Basically, I use invoke-sqlcmd to call a script file by using -inputfile, but if the script file has some execution error (like insert into a table where a column should not be null), the script file will be executed twice. I can see the two executions from profiler as well.
Here is the way to reproduce the issue (My environment: Win 8.1 + SQL2014 + PS 5.0)
create two tables in a database
Use TestDB
create table dbo.s (id int identity primary key, b varchar(50));
create table dbo.t (id int primary key, s_id int, b varchar(50));
alter table dbo.t add constraint fk_t foreign key (s_id) references dbo.s(id)
Now create a sql file (let's call it, c:\temp\t.sql) with the following two lines
insert into dbo.s ( b) select 'hello world'
insert into dbo.t (s_id, b) -- purposely missing id to cause an error
select 1, 'good morning'
Run the following PS cmdlet
invoke-sqlcmd -Server "<my_local_server>" -database TestDB -inputfile "c:\temp\t.sql"
Your PS will return an error, now if you open an SSMS query window and do the following
select * from TestDB.dbo.s
You will see two records there instead of one.
On the other hand, if I run sqlcmd.exe, there is NO such issue, i.e. just one record in dbo.s.
Is there some configuration in SQLPS I missed?
I see you asked this same question on the MSDN Database Engine forum: https://social.msdn.microsoft.com/Forums/en-US/d4167226-2da7-49ec-a5c2-60e964785c2c/powershell-invokesqlcmd-calls-stored-procedure-second-time-after-query-timeout-is-expired. Below is the SMO workaround from that thread.
$SqlServerName = "YourServer";
$DatabaseName = "YourDatabase";
$ScriptFileName = "C:\Scripts\YourSqlScriptFile.sql";
Add-Type -Path "C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.Smo.dll";
$sr = New-Object System.IO.StreamReader($ScriptFileName);
$script = $sr.ReadToEnd();
$sr.Close();
$Server = New-Object Microsoft.SqlServer.Management.Smo.Server($SqlServerName);
$db = $Server.Databases[$DatabaseName];
$db.ExecuteNonQuery($script);
Updating this tread with the fix from Microsoft:
Cumulative Update 2 for SQL Server 2016 SP1
Cumulative Update 4 for SQL Server 2014 Service Pack 2
FIX: "Invoke-sqlcmd" cmdlet executes a query statement multiple times if an error occurs in SQL Server 2014 or 2016
Even i had a similar issue.
Fixed it by adding -QueryTimeout parameter to Invoke-Sqlcmd cmdlet.
Basically it seems that the query somehow times out and due to a bug in Invoke-Sqlcmd cmdlet it tries to insert it again. Strange but try this. Keep the query timeout big enough for the query to execute.
I know this is a old thread, but maybe it can help someone.
I found out if you move your line "select 1, 'good morning'" in your example before the insert statement, which has the exception, it works like intended.
I had a similar Issue with try catch, that the first return value will be ignored when it's an exception, so I had to make sure that the first line was Select 1. Strange bug.
Related
MSSQL lets you do a multi-row UPDATE and INSERT from some source, like another table, or JSON & XML data, etc. Psuedo-example:
INSERT INTO TBL SELECT * FROM SOURCE
Is it possible to use a PowerShell hash table as the source?
#psuedo code
$carHashTable = [pscustomobject]#{
color = 'red'
make = 'geo'
model = 'metro'
year = 1996
insured = 0
}
#here-string
$sql = #"
insert into
SomeDumbTable
select
*
from $carHashTable
"#
Invoke-Sqlcmd -Query $sql
All the examples I've seen online use a foreach loop to do the actual insert or update. Bossmang says loops (in SQL proper) are bad.
The only other option I can think of is using PowerShell to create a temp table in SQL, then use the temp table as the source to do the the multi-insert.
Attempting to answer my own question. If I make an incorrect statement, please correct me.
1) YES. You can convert a hash table using this module created by James Brundage into an object that the SqlServer module can deal with.
#import module that converts PSObjects into a DataTable that SQL can handle (external author)
import-module -name C:\PowerShell\ConvertTo-DataTable.ps1 -Verbose
#import the csv, and convert it to a DataTable
$Jobs = import-csv -Path $Repository\$File -Delimiter '|' | ConvertTo-DataTable
2) For the larger question, can you UPDATE without a foreach loop?, I'm going to say NO, at least not with a SQL #temp table, which was my original intention. Here's what I've observed:
Write-SqlTableData does not appear to support #temp tables
Write-SqlTableData DOES allow me to use the converted PS $object
to write data; however, it only appends data to tables; I don't know
of a way to UPDATE on some key
The only way I was able to achieve this was by gleaning a lot on
StackOverflow, and bringing in the SQL .NET client libraries
System.Data.SqlClient.
Those libraries allowed me to open and maintain a session with SQL so that I could create a #temp table, bulk copy content into it, then finally UPDATEing the real target table.
The SqlServer Modules for PowerShell seems to immediately open and close the session, so any #temp tables created there are only avilable to that session
The session created by the .NET client libraries can be opened and maintained to run multiple queries, but it's separate from sessions created by the SqlServer modules -- PS can't gain access to #temp tables created in .Net sessions, and vice versa.
I'm unable to run 2 SELECT statements from Invoke-sqlcmd.
$sql ="
select SERVERPROPERTY('ProductVersion') Version
select SERVERPROPERTY('ProductLevel') ServicePack, SERVERPROPERTY('edition') Edition"
$server = 'SomeServerName'
Invoke-Sqlcmd -ServerInstance $server -Query $sql
Only the first TSQL statement returns the result.
Note: I can re-write the TSQL to make one statement. That is not the
point. The original script has multiple TSQL commands. Also, I can do
individual invoke-sqlcmd calls, for each statement. However, I'm
trying to understand if this is a limitation of invoke-sqlcmd.
Is this the intended behavior?
Thank you so much.
You've already got an answer that works, but this is more an explanation of what's going on...
Basically, you've got a display / formatting issue rather than a problem with Invoke-SqlCommand.
In your example, your on-screen output probably looks like this:
PS> Invoke-Sqlcmd -ServerInstance $server -Query $sql
Version
-------
15.0.2070.41
PS> _
Note the 3 blank lines after the table - the first blank line is actually the results from the second sql command!
When PowerShell formats a table of items it uses the first item to decide what columns to show - the results from your first command contains just a "Version" column so that's all the table contains. The result from your second command doesn't have a "Version" column so PowerShell displays an empty value in that column, but it also doesn't add a "ServicePack" or "Edition" column to the table for you.
You can override this by specifying the column names with something like this:
PS> Invoke-Sqlcmd -ServerInstance $server -Query $sql | format-table Version, ServicePack, Edition
Version ServicePack Edition
------- ----------- -------
15.0.2070.41
RTM Developer Edition (64-bit)
PS> _
Note there's only 2 blank lines after the table now because the second result has de-cloaked.
If you output the two results separately inside a loop, PowerShell will show two separate tables and will decide what columns to show in each table separately, so you get this, per #Dan Guzman's answer:
Version
-------
15.0.4023.6
ServicePack Edition
----------- -------
RTM Developer Edition (64-bit)
One method is to iterate over the results of Invoke-Sqlcmd to show the multiple result sets:
$results = Invoke-Sqlcmd -ServerInstance $server -Query $sql
for ($i = 0; $i -lt $results.Count; ++$i) {
$results[$i] | Out-Host
}
Output:
Version
-------
15.0.4023.6
ServicePack Edition
----------- -------
RTM Developer Edition (64-bit)
I have to automate one process to get rid of daily effort reduction in our organization. We need to execute multiple scripts on different SQL Server instances and each script contains database name as well.
So initially our client put all the scripts on a particular location and I need to execute each of the scripts and then move the script file to different folder.
After a script got an error, it logged the error and one file has been generated.
After successful execution I need to generate one log file where all the successful results script wise also getting generated. Like when we execute one script in SSMS, after executing the script it generates a message like "1 row affected".
How can I do that?
invoke-sqlcmd -inputfile "E:\test.sql" -serverinstance ".\Your_Instance_Name" -database "user" | out-File -filepath "E:\result.txt"
The "1 row(s) affected" message is generated automatically, unless set nocount is specified.
A client application, such as Invoke-SqlCmd, SSMS and sqlcmd, can do whatever with the row count number. SSMS and sqlcmd print it per default, Invoke-SqlCmd doesn't seem to do that. This is not a bug, though it's certainly a bit surprising.
The simple approach is to issue an explicit select for ##ROWCOUNT. Like so,
insert mydb.schema.table(column1, column2....) values(...); select ##rowcount;
Or, use sqlcmd.exe instead.
Invoke-Sqlcmd -ServerInstance '.' -Database 'MyDB'
-Query 'EXEC SprocA #param1= "value";EXEC SprocB #param1= "value";'
Basically I have my Invoke-SqlCmd running a query that invokes two stored procedures. Both the stored procedures output a bunch of rows.
However if sprocA does not output any results (empty select results or no rows), then the invoke command does not seem to print the output of the second sprocB even if it has data.
If I change the order of the stored procedures in my Invoke-SqlCmd commands query parameter, then this works perfectly and returns the output of the first stored procedure.
If I had three stored procedure calls where the first returns data and the second does not and the third does, it prints output of the first result and third result.
Basically it does not print any output only if the first stored procedure has no output. Seems weird.
Anything I can do to get around this SQL wise ? Could be a PowerShell thing?
I was also able to repro this with two Select statements where one returns data and the other does not.
This is the documented behavior of invoke-sqlcmd
When this cmdlet is run, the first result set that the script returns
is displayed as a formatted table. If subsequent result sets contain
different column lists than the first, those result sets are not
displayed. If subsequent result sets after the first set have the same
column list, their rows are appended to the formatted table that
contains the rows that were returned by the first result set.
It looks like both result sets are actually returned, but not output by defaut.
EG
PS C:\> Invoke-SqlCmd "select 1 a; select 2 b, 3 c;" | % { $_ | Out-Default }
outputs
a
-
1
b c
- -
2 3
Actually, it is hard to believe MS made such a mistake or maybe it's not a mistake. Whatever, when you run Invoke-SqlCmd and with a query like the following,
select * from table1 where id = 1111 -- non-exists id, this select returns nothing
select * from table2 where id = 2222 -- exists id, this select returns something
On SSMS you can see 2 result sets, the first one is empty.
However, the Invoke-SqlCmd doesn't return anything when the first result set is empty, even other result sets are not. My face was like ?_?
Another approach is to write you own invoke SQL function like following to return whatever result sets, even the empty ones.
$sql_Conn = New-Object System.Data.SqlClient.SQLConnection
$sql_Conn.ConnectionString = $sqlConnectionString
$sql_Conn.Open()
$sql_cmd = New-Object system.Data.SqlClient.SqlCommand($Query, $sql_Conn)
$sql_ds = New-Object system.Data.DataSet
$sql_da = New-Object system.Data.SqlClient.SqlDataAdapter($sql_cmd)
[void]$sql_da.fill($sql_ds)
$sql_Conn.Close()
return $sql_ds
Everything is fine until a new problem comes about the keyword GO in your script, you must know it if you use SSMS. The thing is, this GO is not a SQL command. it is just a separator used by SSMS. MS developed codes can handle the GO in a good manner, e.g. SSMS, Invoke-SqlCmd and sqlcmd.exe. If you use your own SQL invoke function you will get syntax issue
Incorrect syntax near 'GO'
While people most likely to ask you to update the SQL script to remove all GO lines, however, things are not always under controlled, normally need to work with different people and teams.
At last, I have to trim the GO in my scripts like the following
$Query = $Query -ireplace "(^|\r|\n)[ \t]*\bGO\b[ \t]*(\r|\n|$)", '$1$2'
https://github.com/LarrysGIT/Invoke-Sql
Of course, the story is not over, the more I am trying to automate SQL related tasks. The more issue found. There are multiple ways to automatically execute SQL script. None of them are perfect so far.
Invoke-Sql (My own script)
* Is able to handle the key separator 'GO'
* Is able to handle duplicate columns
* Fully support multiple result sets, even the first result set is empty
* Unable to handle `Create or alter` keywords if there are contents ahead
* Unable to handle special characters like '194 160' (non-breaking space) in SQL script (edited by some document edit tool, MS word for example)
Invoke-SqlCmd
* Is able to handle the key separator 'GO'
* Is able to handle special characters like 'non-breaking space'
* Unable to handle duplicate columns
* Unable to fully handle multiple result sets (when the first table is empty)
sqlcmd.exe
* Is able to handle all things (briefly tested)
* The returned result sets are plain text, hard to parse
Microsoft.SqlServer.SMO snapin
* The API of SQL server management studio
* Theoretically should be able to handle all cases
* Need to dig more
I am using PostgreSQL 9.
When trying to do this update, row table does not get updated.
$cmd = "UPDATE table1 SET field1 = '$value1' WHERE key_field = '$key_value'; ";
table1 has privileges for PUBLIC to INSERT and UPDATE.
When using pgAdmin III SQL console it does perfectly the job.
Don't use variable parsing (or string concatenation) to build SQL queries;
What does "using PgAdminIII sql console it does perfectly the job" mean? You have pasted the same query in pgAdmin3 and it worked? I very much doubt pgAdmin3 understands PHP and does PHP-style variable parsing as a consequence.
If it was not exactly the same query (most probably it was one with the PHP variables replaced with literals) what was the query you tested in pgAdmin3?
Most probably the reason the update is ineffective is that there are no rows that satisfy your WHERE clause.
$cmd = "UPDATE table1 SET field1 = '$value1' WHERE key_field = '$key_value'";
Now try there was an extra ;