I have C# code that cycles through .sql files and executes what's inside them to set up a database.
One .sql file is basically as follows:
DROP PROCEDURE IF EXISTS myProc;
DELIMITER $$
CREATE PROCEDURE myProc()
BEGIN
-- procedure stuff goes here
END $$
DELIMITER ;
CALL myProc();
When I input this into the MySQL Query Browser's script window, it runs perfectly... over and over again, just as one would want it to.
However, if I put the string into my IDbCommand and execute it...
connection.Open(); // An IDbConnection
IDbTransaction transaction = connection.BeginTransaction();
using (IDbCommand cmd = connection.CreateCommand())
{
cmd.Connection = connection;
cmd.Transaction = transaction;
cmd.CommandText = line;
cmd.CommandType = CommandType.Text;
try
{
cmd.ExecuteNonQuery();
}
catch (SqlException ex)
{
transaction.Rollback();
return false;
}
}
transaction.Commit();
connection.Close();
... I get the dreaded exception 1064...
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'DELIMITER $$ CREATE PROCEDURE myProc() BEGIN...
So, the question is... why does MySQL let me do this with no problems, but when I try to run it from C#, it fails? And of course the second question is how I'm supposed to fix it.
For those looking for a quick snippet...
var connectionString = #"server=ChangeMe;user=ChangeMe;pwd=ChangeMe;database=ChangeMe;";
var scriptText = File.ReadAllText(#"C:\script.sql");
using (var connection = new MySqlConnection(connectionString))
{
var script = new MySqlScript(connection, scriptText);
connection.Open();
script.Execute();
}
I think what you are looking for is this: "Bug #46429: use DELIMITER command in MySql.Data.MySqlClient.MySqlScript"
Some of the commands in your script are interpreted bt the mysqlk client excutable before the SQL is sent to the server.
See mysql commands
In effect all the delimiters
To interpret the script from C# you will have to write code that knows what the command is, you cannot just pass the commands line by line to the server.
e.g. you have 3 commands in that file and so want to call ExecuteNonQuery only 3 times
and from paracycle's coment you need to use the MySqlScript class
Related
SQL Server: I have copied and saved a stored procedure in a database. During that process, it has escaped the line feeds and tabs into \n and \t characters throughout the stored procedure text so it looks like:
SQL = "create PROCEDURE ScrambleNames\n\t#removeAdmins bit \nAS\nBEGIN\n\t-- SET NOCOUNT ON added to prevent..." just shortened for clarity
using (DbConnection dbcn = dbf.CreateConnection())
{
dbcn.ConnectionString = cnxn;
dbcn.Open();
using (DbCommand dbcmd = dbcn.CreateCommand())
{
dbcmd.CommandType = CommandType.Text;
dbcmd.CommandText = SQL;
dbcmd.ExecuteNonQuery();
}
}
I want to read that SQL statement with C# and use it to create an actual stored procedure in another database by executing that command. Obviously, when I try and execute it without any changes, it says there is a syntax error near \ and fails. I've tried replacing the \n and \t with actual binary characters in the string but it fails with more errors:
Incorrect syntax near '1'... etc.
So, how can I read the text from the database and keep the line feeds and tabs in the stored procedure I'm trying to create?
Use verbatim strings with # to support multiline strings, like this
SQL = #"create PROCEDURE ScrambleNames
#removeAdmins bit
AS BEGIN
-- comment here
SELECT * FROM";
using (DbConnection dbcn = dbf.CreateConnection())
{
dbcn.ConnectionString = cnxn;
dbcn.Open();
using (DbCommand dbcmd = dbcn.CreateCommand())
{
dbcmd.CommandType = CommandType.Text;
dbcmd.CommandText = SQL;
dbcmd.ExecuteNonQuery();
}
}
Example to explain verbatim string:
normal_string = "Line1 \n Line2 \n line3";
same_as_verbatim_string = #"Line1
Line2
line3";
I am working on a big solution in C#. This solution uses different oracle databases and is checked in in TFS. Actually I develop a small library which can deploy the SQL Scripts in the solution to the used databases. The deployment of the application is handled with TFS and in the future my tool should be included in the build process. For the Oracle Connection an execution of the statements I use Oracle.ManagedDataAccess.Client.
If there are only several DDL or DML statements in the SQL Files the first tests were successful. But there are a few special special things I have to handle, like SET DEFINE OFF. This is no real SQL Statement and if I run the Script with ORacle SQL*PLus there is no problem. In the beginning I tried to execute the whole script but this doesn't work because multiple statements were put in one line by my application and the only solution I found was the splitting of the file and single execution.
Example:
/*************************************************************************************************************************
##NAME ScriptName.sql
##DESCR ScriptDescription
*************************************************************************************************************************/
Create table tmp_thm_Test
( tmp_thm_Test_id number(8)
, text varchar2(200));
Create table tmp_thm_Test2
( tmp_thm_Test2_id number(8)
, text varchar2(200));
This is an example content for a script. Also there can be Insert (Set Define off is needed while having the & in strings), Update Statements. Also Begin/End Scripts.
If there are only DDL and DML Statements I can split them but Begin/End parts are not splittable. At the beginning I thought I can deploy a script like in the past with SQL*Plus (#Scriptname.sql). Read the whole script an execute it.
Any ideas?
Since I've worked with Oracle in C# before I kind of understand you even with the lack of concrete samples.
SET DEFINE OFF never worked for me in C#, so I simply use query = query.Replace("'","''");. You could also use command parameters like the one below to avoid exceptions.
string query = string.Format(
#"UPDATE CardStatus
SET DateExpired = #dateExpired,
ModifiedBy = #modifiedBy,
ModifiedOn = #modifiedOn
WHERE
IDNo = #idNo");
List<OracleParameter> parameters = new List<OracleParameter>();
OracleParameter pIdNo = new OracleParameter("idNo", OracleDbType.Varchar2);
pIdNo.Value = idNo;
OracleParameter pDateExpired = new OracleParameter("dateExpired", OracleDbType.Date);
pDateExpired.Value = dateExpired;
OracleParameter pModifiedBy = new OracleParameter("modifiedBy", OracleDbType.Varchar2);
pModifiedBy.Value = "SIS";
OracleParameter pModifiedOn = new OracleParameter("modifiedOn", OracleDbType.Date);
pModifiedOn.Value = DateTime.Now;
parameters.Add(pIdNo);
parameters.Add(pDateExpired);
parameters.Add(pModifiedBy);
parameters.Add(pModifiedOn);
bool result = _DAL.ExecuteNonQuery(query, parameters.ToArray());
_DAL is simply my helper class for data access, it manages the query executions placing it inside a single transaction so that I get to decide whether to commit or rollback the changes of single to multiple queries.
public bool ExecuteNonQuery(string query, object[] parameters, [CallerMemberName] string callerMemberName = "")
{
bool success = false;
try
{
using (OracleCommand command = new OracleCommand())
{
command.Connection = _connection;
command.Transaction = _transaction;
command.CommandText = query;
command.CommandType = CommandType.Text;
command.Parameters.AddRange(parameters);
command.ExecuteNonQuery();
}
this.AuditSQL(query, string.Empty);
success = true;
}
catch (OracleException ex)
{
this.AuditSQL(query, ex.Message, callerMemberName);
if (ex.Number == 54) // SELECT .. FOR UPDATE NOWAIT failed.
{
throw new RowLockException();
}
}
return success;
}
I am not sure if you are interested in looking at my _DAL class but I provided you a snippet to give you an idea of what you can do.
EDIT: Since you've mentioned that the scripts already exists then it is easier, you just have to make sure that there is no ' character within the script to make oracle ask for a variable value and all batch queries must have BEGIN and END;.
Example:
BEGIN
-- INSERT
-- UPDATE
-- INSERT
... etc.
END;
Now, what exactly is your problem? You can read the query from the file and replace all SET DEFINE OFF (as well as other statements you deem unnecessary) with string.Empty and replace all ' with '' if needed. Worst case, you would be using regex to identify and remove unwanted statements.
I have a stored procedure that deletes rows. Sample code:
CREATE PROCEDURE [dbo].[ap_um_delete_tems_grid]
(
#agency char(3)
, #subagency char(1)
, #grid_id int
, #role_id int
)
AS
DELETE FROM [grid_setup]
WHERE agency = #agency
AND subagency = #subagency
AND gs.grid_id = #grid_id
AND role_id = #role_id
If I execute this SProc directly from Sql Mgmt Studio it works fine. However, if I attempt to do it in C# code using a transaction, I get a very interesting SqlException:
Incorrect syntax near 'ap_um_delete_tems_grid'
The exception detail says it occurs on line number 1. The C# code is:
using (SqlConnection connection = new SqlConnection(conn.TemsConnectionString))
{
connection.Open();
SqlCommand command = connection.CreateCommand();
SqlTransaction transaction;
// Start the local transaction
transaction = connection.BeginTransaction("GridReplaceTransaction");
command.Connection = connection;
command.Transaction = transaction;
try
{
command.CommandText = "ap_um_delete_tems_grid";
SqlParameter parm = new SqlParameter();
// Adding the parameters
int cnt = command.ExecuteNonQuery();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
}
}
The code above worked before I put it into a transaction. Why am I getting the syntax error? Perhaps because there needs to be something in the SProc to support the transaction? Or am I messsing something else up?
NOTE: Please don't try to tell me I don't need a transaction for a single delete. The above is part of a multi-step process, namely adding new rows after the delete. Thanks.
You are missing:
command.CommandType = CommandType.StoredProcedure;
Otherwise, it uses the default of CommandType.Text and treats "ap_um_delete_tems_grid" as a statement rather than the name of a stored procedure.
I'm new in C# programming, so I'll appreciate if anyone can help me. I know there are similar question but I still can't find the solution for my problem. I'm developing a mock system, where when user bought the product, the system will store all the transaction details. the problem is, I cannot insert the data into the database. Here's the code:
using (SqlConnection conn = new SqlConnection
(ConfigurationManager.ConnectionStrings["database"].ConnectionString))
{
string QueryA = "#Insert into TransDetails(AccountNumber,Amount,Provider"
+ ",Mobile Number,TransNum,TransDate, Status) "
+ " Values (#AccountNumber,#Amount,#Provider,#Mobile Number,"
+ "#TransNum,#TransDate,#Status";
using (SqlCommand cmd = new SqlCommand("InsertRecord", conn))
{
conn.Open();
cmd.CommandType = CommandType.Text;
cmd.CommandText = QueryA;
cmd.Parameters.AddWithValue("#AccountNumber", acc.Text);
cmd.Parameters.AddWithValue("#Amount", lblAmount.Text);
cmd.Parameters.AddWithValue("#Provider", lblProvider.Text);
cmd.Parameters.AddWithValue("#Mobile Number", lblNumber.Text);
cmd.Parameters.AddWithValue("#TransNum", lblTrans.Text);
cmd.Parameters.AddWithValue("#TransDate", lblDate.Text);
cmd.Parameters.AddWithValue("#Status", status.Text);
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch
{
lblMessage.Text = "Error";
}
finally
{
conn.Close();
}
}
}
and the stores procedures are as follows:
ALTER PROCEDURE InsertRecord1
#AccountNumber int,
#Amount nchar(10),
#Provider nchar(10),
#MobileNumber int,
#TransNum nchar(10),
#TransDate date,
#Status nchar(10)
AS
Insert into TransDetails(AccountNumber,Amount,Provider,MobileNumber,TransNum,TransDate,Status)
Values (#AccountNumber,#Amount,#Provider,#MobileNumber,#TransNum,#TransDate,#Status)
return
Really appreciate any help.
P/S: i dont know why the beginning of the stored procedures started with "alter".
I may be reading it wrong, but it looks like your stored procedure is not used at all. Try commenting out "cmd.CommandText = QueryA;" and substitute "cmd.CommandText = "InsertRecord1";" and change CommandType to StoredProcedure.
QueryA, by the way, is missing a paren at the end. However, the whole thing is unnecessary since you have a stored procedure that does the same thing and it's almost always preferable to use a stored procedure rather than embedded DML.
You must escape Mobile Number while brackets
Insert into TransDetails(AccountNumber,Amount,Provider,[Mobile Number],...
and remove the space in your parameter
...,#MobileNumber,#TransNum,#TransDate,#Status
and change the paramname in your command parameter
cmd.Parameters.AddWithValue("#MobileNumber", lblNumber.Text);
but seeing your stored procedure, the column Mobile Number has no space between it. Is it a typo error in your query on QueryA? If it is, then remove the space on it (also on parameter name)
Insert into TransDetails(AccountNumber,Amount,Provider,MobileNumber,...
or
change your CommandType.Text to CommandType.StoredProcedure and remove this line,
cmd.CommandText = QueryA;
You're using the wrong overload of the SqlCommand constructor. According to MSDN:
new SqlCommand(string, SqlConnection) Initializes a new instance of the SqlCommand class with the text of the query and a SqlConnection.
What you need to do is either set your CommandType for the sql command to CommandType.StoredProcedure and not use QueryA, or initialize the sql command with QueryA and not make use of your stored procedure.
As you can see there is # at the start of your SQL Statement.
Also you are not really using the Store Procedure.
You can Try this:
using (SqlConnection conn = new SqlConnection (ConfigurationManager.ConnectionStrings["database"].ConnectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand("InsertRecord1", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#AccountNumber", acc.Text);
cmd.Parameters.AddWithValue("#Amount", lblAmount.Text);
cmd.Parameters.AddWithValue("#Provider", lblProvider.Text);
cmd.Parameters.AddWithValue("#Mobile Number", lblNumber.Text);
cmd.Parameters.AddWithValue("#TransNum", lblTrans.Text);
cmd.Parameters.AddWithValue("#TransDate", lblDate.Text);
cmd.Parameters.AddWithValue("#Status", status.Text);
try
{
cmd.ExecuteNonQuery();
}
catch
{
lblMessage.Text = "Error";
}
finally
{
conn.Close();
}
}
Tho I don't use SQL Commands, Adapters...etc. to access the data from the SQL Database. I prefer Microsoft Data Access ApplicationBlocks which is easy-to-use library provided by Microsoft to access data from SQL Server.
Download
You can download it here http://download.microsoft.com/download/VisualStudioNET/daabref/RTM/NT5/EN-US/DataAccessApplicationBlock.msi
Introduction
https://web.archive.org/web/20210304123854/https://www.4guysfromrolla.com/articles/062503-1.aspx
I have many files for procedures, views, functions, etc.
I want to execute these files (creating components) in appropriate database on SQL Server 2005/2008.
Also the point is I want to execute them using C#.
Another point to mention, I want the application to be such that I can execute this files on a remote SQL Server too. Also client machine may not have osql,sqlcmd command tool.
Can someone please guide me on this.
This depends on what sort of files they are. If, for example, they only contain actual T-SQL commands (and aren't batch files that you'd run in, say, SSMS, which would contain a batch separator like GO), then you just need to create a connection, a command, then read the contents of the file and use that to populate the CommandText property of the command.
For example:
void ExecuteFile(string connectionString, string fileName)
{
using(SqlConnection conn = new SqlConnection(connectionString))
{
string data = System.IO.File.ReadAllText(fileName);
conn.Open();
using(SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = data;
cmd.ExecuteNonQuery();
}
}
}
If it's a batch file, you'll need to split the file into individual batches and process those individually. The simplest method is just to use string.Split, but bear in mind that it won't respect SQL parsing rules when it splits (for example, if GO appears within a SQL statement, it's going to split the command up into two batches, which will obviously fail).
More generally, you can see what you'd need to do here by modifying the code in this way:
string[] batches = SplitBatches(System.IO.File.ReadAllText(fileName));
conn.Open();
using(SqlCommand cmd = conn.CreateCommand())
{
foreach(string batch in batches)
{
cmd.CommandText = batch;
cmd.ExecuteNonQuery();
}
}
The implementation of a function called SplitBatches is up to you.
Typically, the simplest way is to split the script on the "GO" statement and execute each item separately. This solution will not work if the script contains a GO statement within a comment.
private readonly Regex _sqlScriptSplitRegEx = new Regex( #"^\s*GO\s*$", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled );
public void ExecuteSqlScript( string scriptText )
{
if ( string.IsNullOrEmpty( scriptText ) )
return;
var scripts = _sqlScriptSplitRegEx.Split( scriptText );
using ( var conn = new SqlConnection( "connection string" ) )
{
using ( var ts = new TransactionScope( TransactionScopeOption.Required, new TimeSpan( 0, 10, 0 ) ) )
{
foreach ( var scriptLet in scripts )
{
if ( scriptLet.Trim().Length == 0 )
continue;
using ( var cmd = new SqlCommand( scriptLet, conn ) )
{
cmd.CommandTimeout = this.CommandTimeout;
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
}
}
ts.Complete();
}
}
}
Normally all you have to do is just execute them with SqlCommand.ExecuteNonQuery, one batch at a time. That leaves you the task of splitting the scripts into batches, using the currently set batch delimiter (usually GO). The free library dbutilsqlcmd can be used to handle SQL scripts, as it processes not only the delimiter, but SQLCMD extensions as well (:setvar, :connect etc).
Using standard ADO would "work" -- SQL DDL can be sent using SqlCommand.ExecuteNonQuery, for instance.
...but I'd recommend using a Database Project (with a remote staging deploy or VSDBCMD) or one of the tools such as SQLCMD (perhaps in conjunction with a PowerShell script :-) or SQL Management Studio, etc. They are designed for this sort of stuff.
You could read in the commands using a TextReader, then use ExecuteNonQuery with the TextReader results as the command.