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.
Related
I have this SQL file with the CREATE statements for 697 stored procedures in it, but unfortunately it won't execute due to a syntax problem.
The script requires a GO statement between every CREATE PROCEDURE call.
The question is: how do I add GO before every CREATE PROCEDURE statement?
I am looking to achieve this through the use of PowerShell code.
The Select-String might guide us in the right direction as it is able to find the 697 stored procedures in the SQL file. The below returned a count of 697. But not sure how to use this to add text in front of every finding.
(Select-String -Path $sqlFile -Pattern "CREATE PROCEDURE" -AllMatches).Matches.Count
I also tried to replace text in the file with the below command
(Get-Content $sqlFile).replace('CREATE PROCEDURE', ' GO CREATE PROCEDURE') | Set-Content $sqlFile
This however resulted in an error when executing the SQL script:
A fatal scripting error occurred. Incorrect syntax was encountered while parsing GO
Thank you for the help.
The solution is found. Use the `ncharacter.
(Get-Content $sqlFile).replace('CREATE PROCEDURE', " GO `n CREATE PROCEDURE") | Set-Content $sqlFile
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
In powershell I would like to use Bulk copy to export and then import data from one table to another in the same database. The DB server is not local on my dev machine so I am using the /S as the Database instance name. The error that I am getting is A valid table name is required for in, out, or format options not sure what I have incorrect with the command. I am using Windows Authentication for studio.
PS C:\Users\dev> $psCommand = "bcp $($db).$($schema).$($table) out $path /S$($server) /t /c, -T"
PS C:\Users\dev> Invoke-Expression $psCommand
A valid table name is required for in, out, or format options.
I'm trying to do something like:
"If it exists, use it. If not, create it."
"If it exists, delete it. If not, create it."
One place it's definitely choking is the use it command - because if it DOES NOT EXIST - it chokes on the use command EVEN THOUGH that command will not run.
Here's more explanation:
I have a SQL Server script where I create a database and then I use the database.
The script will not run
because the use database command is invalid
because the database does not exist
but it will exist after the first command executes
but it doesn't matter because it doesn't exist NOW so the script will not run.
How do I put code in there that tries to use a database that might not exist?
How do I put code in there that will cause an error if run directly but WILL NOT RUN unless conditions are appropriate.
Please see the attached images.
Here's the code so you don't have to type it...
-- SQL SERVER: We can't run this script because CFPT does not exist.
-- ME: But it WILL exist after the first command runs
-- SQL SERVER: That does not matter - at THIS point in the code... it does not exist... tough luck
-- CREATE THE DATABASE
create database CFPT
-- USE THE DATABASE
USE CFPT
use master
drop database CFPT
Second code snippet:
-- SQL SERVER: We can't run this script because CFPT does not exist.
select db_id('CFPT') -- this just lets us see what the IF statement is going to have to deal with
IF db_id('CFPT') is null
begin
print 'DESIRED DB DOES NOT EXIST'
return
end
else
begin
use CFPT -- this line of code makes the whole script just not run.
end;
-- doesn't want to work - chokes on the use databasename (when the database does not exist)
(EDIT 1 start ////////////////////////////////////////////////////////////////////////////////////)
A third image was added with this edit - The SECOND image shows that the if/then/else statement will not work. The 3rd image shows that the database CFPT is not in the database list (left side of image) and the select statement was run (top highlighed code) and the results of that select (bottom red circle)
How do I get the if/then/else statement to work? (Because the THEN will not run if the conditions are not favorable shall-we-say)
(for some reason the red wavy lines are not showing up - they should be but they aren't - hmmm)
(EDIT 1 end ////////////////////////////////////////////////////////////////////////////////////)
(EDIT 2 start ////////////////////////////////////////////////////////////////////////////////////)
In relation to this question - trying to segregate commands that would normally fail but will not be attempted to be executed unless conditions are just right..... (see 4th image below) I'm segregating some commands with an IF statement (IF 1=2) but SQL Server is going into that IF statement even though the condition is false. Why is that?
(EDIT 2 end ////////////////////////////////////////////////////////////////////////////////////)
Try this ...
-- CREATE THE DATABASE
create database CFPT
GO
-- USE THE DATABASE
USE CFPT
use master
drop database CFPT
The GO command is a batch terminator, it separates the command to create the database from the command to use it.
See https://msdn.microsoft.com/en-us/library/ms188037.aspx
and
What is the use of GO in SQL Server Management Studio & Transact SQL?
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.