I have a Winforms app that has to migrate data from SQL to MySQL. Part of the functionality requires preparing the target database by adding the required tables (and dropping existing tables if necessary).
I'm looking for the most efficient way to determine if the table was created successfully. This question's answer suggested that executing the query would return an integer value greater than 0 if it was successful. Ideally, I'd like to execute the command and use a return value rather than running another query. Is this possible?
My current code:
MySqlConnection myConnection = new MySqlConnection(ConnectionString);
string sql = #" DROP TABLE IF EXISTS `sf_root_items`;
CREATE TABLE `sf_root_items` (
`ID` varchar(255) NOT NULL,
`LoweredName` varchar(255) DEFAULT NULL,
`MenuName` varchar(255) DEFAULT NULL,
`Title` varchar(255) DEFAULT NULL,
`Description` varchar(255) DEFAULT NULL,
`PageType` varchar(255) DEFAULT NULL,
`ExternalUrl` varchar(255) DEFAULT NULL,
PRIMARY KEY(`ID`)
)";
MySqlCommand cmd;
try
{
if (myConnection.State != ConnectionState.Open)
{
myConnection.Close();
myConnection.Open();
}
cmd = myConnection.CreateCommand();
cmd.CommandText = sql;
int output = cmd.ExecuteNonQuery();
// a value greater than 0 means execution was successful
if (output > 0)
{
DBPrepDone = "Table created";
}
else
{
DBPrepDone = "There was an error";
}
myConnection.Close();
}
catch (Exception ex)
{
DBPrepDone = ex.ToString();
}
Here is a description of ExecuteNonQuery return values from Microsoft site
https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executenonquery%28v=vs.110%29.aspx
Although the ExecuteNonQuery returns no rows, any output parameters or
return values mapped to parameters are populated with data. For
UPDATE, INSERT, and DELETE statements, the return value is the number
of rows affected by the command. When a trigger exists on a table
being inserted or updated, the return value includes the number of
rows affected by both the insert or update operation and the number of
rows affected by the trigger or triggers. For all other types of
statements, the return value is -1. If a rollback occurs, the return
value is also -1.
To make your query work you should add check of existing your table, for example
string sql = #" DROP TABLE IF EXISTS `sf_root_items`;
CREATE TABLE `sf_root_items` (
`ID` varchar(255) NOT NULL,
`LoweredName` varchar(255) DEFAULT NULL,
`MenuName` varchar(255) DEFAULT NULL,
`Title` varchar(255) DEFAULT NULL,
`Description` text,
`PageType` varchar(255) DEFAULT NULL,
`ExternalUrl` varchar(255) DEFAULT NULL,
PRIMARY KEY(`ID`)
)";
string sql_check = #" SELECT count(*)
FROM information_schema.TABLES
WHERE (TABLE_NAME = 'sf_root_items')
AND (TABLE_SCHEMA = '" + WP_db.Text +"')";
MySqlCommand cmd;
try
{
if (myConnection.State != ConnectionState.Open)
{
myConnection.Close();
myConnection.Open();
}
cmd = myConnection.CreateCommand();
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
//call check if table was created
cmd = myConnection.CreateCommand();
cmd.CommandText = sql_check;
var test = cmd.ExecuteScalar();
int output;
int.TryParse(test.ToString(), out output);
// a value greater than 0 means execution was successful
if (output > 0)
{
DBPrepDone = "Table 'sf_root_items' has been created";
}
else
{
DBPrepDone = "There was an error";
}
myConnection.Close();
}
catch (Exception ex)
{
DBPrepDone = ex.ToString();
}
Related
I need to update the value in MySQL DB, so in order to do it I use procedure.
There is my DB
CREATE TABLE `sessions` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`manager_name` VARCHAR(1024) NOT NULL COLLATE 'utf8_general_ci',
`created_date` DATETIME NOT NULL,
`num_works` INT(10) NOT NULL DEFAULT '0',
`num_jobs` INT(10) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=306
;
There is my procedure
CREATE DEFINER=`root`#`localhost` PROCEDURE `session_update_works_num`(
IN `id` INT,
IN `num_works` INT
)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT 'use this procedure to update works num'
BEGIN
UPDATE `gmdb`.`sessions` SET `num_works`=num_works WHERE `id`=id;
END
There is how I use it in my C# code
protected override void Execution()
{
using (MySqlConnection conn = DBConnection.Instance.Connection)
{
try
{
conn.Open();
using (MySqlCommand cmd = new MySqlCommand())
{
cmd.CommandText = PROCEDURE_UPDATE_WORKS_NUM;
cmd.Connection = conn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue($"#{Constants.SESSION_TABLE_ID}", SessionSingleton.Instance.SessionId);
cmd.Parameters.AddWithValue($"#{Constants.SESSION_TABLE_NUM_WORKS}", m_worksNum);
cmd.ExecuteNonQuery();
SetTaskStatus(State.COMPLETED);
};
}
catch (Exception e)
{
ErrorMsg += $"ERROR: while execute procedure : {PROCEDURE_UPDATE_WORKS_NUM}, error: {e.ToString()}";
SetTaskStatus(State.FAILED);
}
}
}
The problem is that if for example, I have a few lines in my DB
After the procedure will be executed num_works field will be changed for all the lines.
But if I change the code and use a query instead of a procedure all works fine:
protected override void Execution()
{
using (MySqlConnection conn = TV_DP_DBConnection.Instance.Connection)
{
try
{
conn.Open();
using (MySqlCommand cmd = new MySqlCommand())
{
string query = $"UPDATE `gmdb`.`sessions` SET `num_works`={m_worksNum} WHERE `id`={SessionSingleton.Instance.SessionId};";
cmd.CommandText = query;
cmd.Connection = conn;
cmd.ExecuteNonQuery();
SetTaskStatus(State.COMPLETED);
};
}
catch (Exception e)
{
ErrorMsg += $"ERROR: while execute procedure : {PROCEDURE_UPDATE_WORKS_NUM}, error: {e.ToString()}";
SetTaskStatus(State.FAILED);
}
}
}
What is the problem here? How is it possible that I pass to procedure exact id WHERE 'id'=id but all the lines have an effect?
I think CrowCoder is right, variable names in stored procedure are the same as column names. Change variable names both in SP and code, and try again.
I calling a stored procedure and it has an int return value. However there is an error on returning the value back to my back end.
public async Task<string> CreatePortfolio(Portfolio portfolio)
{
string statusMessage;
using (SqlConnection conn = new SqlConnection(Connection))
{
SqlParameter returnValue = new SqlParameter(); //Holds the bit that determines if insert was successful or not
SqlCommand command;
command = new SqlCommand();
command.Connection = conn;
conn.Open();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "USP_Add_Portfolio";
command.Parameters.AddWithValue("#portfolioName", portfolio.PortfolioName);
command.Parameters.AddWithValue("#description", portfolio.Description);
command.Parameters.AddWithValue("#createID", portfolio.CreateID);
command.Parameters.AddWithValue("#updateID", portfolio.UpdateID);
command.Parameters.AddWithValue("#statusMessage", SqlDbType.NVarChar).Direction = ParameterDirection.Output;
returnValue.Direction = ParameterDirection.ReturnValue;
command.Parameters.Add(returnValue);
int i = await command.ExecuteNonQueryAsync().ConfigureAwait(false);
if(i == 1)
{
statusMessage = command.Parameters["#statusMessage"].Value.ToString();
}
else
{
statusMessage = "Error while adding please contact your administrator";
}
}
return statusMessage;
}
This is the stored procedure:
create procedure USP_Add_Portfolio
(#portfolioName as nchar(30) = null,
#description as nvarchar(200) = null,
#createID as nvarchar(40) = null,
#updateID as nvarchar(40) = null,
#statusMessage as nvarchar(max) output)
as
declare #success as int = 0
if #portfolioName is null
raiserror('Stored procedure USP_Add_Portfolio - Missing Parameter #portfolioName', 16,1)
else if exists( select * from Portfolio where [Portfolio_Name] = #portfolioName COLLATE SQL_Latin1_General_CP1_CI_AS)
begin
set #statusMessage = rtrim(#portfolioName) + ' already exists please try another portfolio name'
raiserror('Stored procedure USP_Add_Portfolio - Already exists #portfolioName', 16,1)
return 0
end
else if #createID is null
raiserror('Stored procedure USP_Add_Portfolio - Missing Parameter #Create_ID', 16,1)
else if #updateID is null
raiserror('Stored procedure USP_Add_Portfolio - Missing Parameter #Update_ID', 16,1)
else
begin
insert into Portfolio ([Portfolio_Name], [Long_Description], [Create_ID], [Create_TS], [Update_ID], [Update_TS])
values (#portfolioName, #description, #createID, getdate(), #updateID, getdate())
--Check to see if insert worked
set #statusMessage = case when ##ROWCOUNT = 1 then 'Successfully added ' + #portfolioName else 'Unable to add please try again' end
set #success = case when ##ROWCOUNT = 1 then 1 else 0 end
end
return #success
go
The stored procedure finishes and it adds the new record but it errors on
int i = await command.ExecuteNonQueryAsync().ConfigureAwait(false);
Error:
expecting an int but gets nvarchar
ExecuteNonQuery (not worrying about the async for the moment...) returns the number of rows affected for UPDATE, INSERT, DELETE, and -1 otherwise. It does NOT return information directly from the stored procedure.
In your case above, I think you should call the "await" without the "int i =" and not worry about the return value from the ExecuteNonQueryAsync call. Instead, after the value, look at the value in returnValue.Value, which would be the value of the "return" parameter. It is an object, so verify the type or use Convert.ToInt32().
This doesn't look correct because SqlDbType.NVarChar is an enumeration value:
command.Parameters.AddWithValue("#statusMessage", SqlDbType.NVarChar).Direction = ParameterDirection.Output;
What happens if you use this instead:
command.Parameters.Add(new SqlParameter("#statusMessage", SqlDbType.NVarChar, -1)).Direction = ParameterDirection.Output;
I have this store procedure in my db:
ALTER procedure [dbo].[AddUpdateCams]
#CamImage_UID uniqueidentifier = NULL,
#CamImage_Url1 nvarchar(50) = NULL,
#CamImage_Url2 nvarchar(50) = NULL,
#CamImage_StatusID int = 10,
#CamImage_ItemNr nvarchar(50) = NULL,
#CamImage_OrderNr nvarchar(50) = NULL
as
begin
set nocount on;
if #CamImage_UID is null
begin
set #CamImage_UID = NEWID()
insert into dbo.CamImage (CamImage_UID,CamImage_Url1, CamImage_Url2,
CamImage_StatusID, CamImage_CreateDate, CamImage_ItemNr, CamImage_OrderNr)
values (#CamImage_UID, #CamImage_Url1, #CamImage_Url2, #CamImage_StatusID,
GETDATE(), #CamImage_ItemNr, #CamImage_OrderNr)
end
else
begin
update CamImage
set CamImage_StatusID = #CamImage_StatusID
where CamImage_UID = #CamImage_UID
end
select * from CamImage where CamImage_UID = #CamImage_UID
end
I am trying to execute this store procedure by using SqlDataAdapter and DataSet, however when executed, my DataSet does not return anything and the problem doesnt seem to be in the Store Procedure because if I executed through sql studio, it doesnt run into any problems.
public DataSet runSPDataSet(string _storedProcedure)
{
DataSet _dataSet = new DataSet();
if (validateSP(_storedProcedure))
{
SqlConnection _sqlConnection = new SqlConnection(_connectionString);
SqlCommand _sqlCommand = new SqlCommand(_storedProcedure, _sqlConnection);
_sqlCommand.CommandType = CommandType.StoredProcedure;
if(_sqlParameters.Count != 0)
{
foreach (var item in _sqlParameters)
{
_sqlCommand.Parameters.Add(item.ParameterName, item.SqlDbType).Value = item.Value;
}
}
SqlDataAdapter _dataAdapter = new SqlDataAdapter(_sqlCommand);
try
{
_sqlConnection.Open();
_sqlCommand.ExecuteNonQuery();
_dataAdapter.Fill(_dataSet);
return _dataSet;
}
catch (Exception ex)
{
throw ex;
}
finally
{
_sqlConnection.Dispose();
}
}
return null;
}
I am trying to insert data into a SQL Server table, but it is not allowing me to do so and throws an error. I think the error is from the role I don't know how to fix it; please I need your help - thank you.
This is the member table that I am using:
CREATE TABLE [dbo].[Member]
(
[Member_Username] NVARCHAR (50) NOT NULL,
[Password] NVARCHAR (25) NOT NULL,
[Role] NVARCHAR (10) NULL,
[FirstName] NVARCHAR (50) NOT NULL,
[LastName] NVARCHAR (50) NOT NULL,
[Gender] NVARCHAR (8) NOT NULL,
[Email] NVARCHAR (50) NULL,
[DateOfBirth] DATE NOT NULL,
PRIMARY KEY CLUSTERED ([Member_Username] ASC)
);
And this is the error I get when inserting the values into the table:
System.Data.SqlClient.SqlException:
The parameterized query '(#memberU nvarchar(1), #pwd nvarchar(1), #role nvarchar(4000), #fna' expects the parameter '#role', which was not supplied.
This is the member class that I have for inserting the user in the database table:
public void AddMember()
{
// Open database connection
SqlConnection conn = new SqlConnection();
conn.ConnectionString = Config.GetConnectionStr();
conn.Open();
// Prepare SQL command with parameters
string sql = "INSERT INTO Member VALUES (#memberU, #pwd, #role, #fname, #lname, #gender, #email, #dob)";
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("memberU", this.Member_Username);
cmd.Parameters.AddWithValue("pwd", this.Password);
cmd.Parameters.AddWithValue("role", this.Role);
cmd.Parameters.AddWithValue("fname", this.FirstName);
cmd.Parameters.AddWithValue("lname", this.LastName);
cmd.Parameters.AddWithValue("email", this.Email);
// handling null values for gender and date of birth column
if (this.Gender != null)
{
cmd.Parameters.AddWithValue("gender", this.Gender);
}
else
{
cmd.Parameters.AddWithValue("gender", DBNull.Value);
}
if (this.DateofBirth != null)
{
cmd.Parameters.AddWithValue("dob", this.DateofBirth);
}
else
{
cmd.Parameters.AddWithValue("dob", DBNull.Value);
}
// Execute command
cmd.ExecuteNonQuery();
}
And this is the sign up button:
protected void btnSignUp_Click(object sender, EventArgs e)
{
if (Page.IsValid)// assuming you have done validations using validation controls
{// c create a new object of type member and set all it's properties to values from controls
Members user = new Members();
//reading required values
user.FirstName = txtFirstName.Text;
user.LastName = txtLastName.Text;
user.Member_Username = txtUserName.Text;
user.Password = txtPassword.Text;
user.Email = txtEmail.Text;
user.Gender = rdoGender.SelectedValue;
//reading values that allow null in the database (date of birth)
if (string.IsNullOrEmpty(txtDOB.Text))
{
user.DateofBirth = null;
}
else
{
user.DateofBirth = DateTime.Parse(txtDOB.Text);
}
//call the addMember method
user.AddMember();
//redirect the user to homePage
Response.Redirect("Login.aspx");
}
}
can you try when you add parameters like ( cmd.parameters.addwithvalue("#role",value).
I have a couple of pages for booking and each page saves data. For example page one adds the destination to the database, page two is selecting amount of passengers.
I have a table to store all this:
CREATE TABLE [dbo].[Transactions] (
[cardNumber ] NCHAR (10) NULL,
[Cost] NCHAR (10) NULL,
[Passengers] NCHAR (10) NULL,
[Destination] NCHAR (10) NULL
);
On the destination page I am using the following code to input the destination to the database:
protected void Button2_Click1(object sender, EventArgs e)
{
try
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DatabaseConnectionString"].ConnectionString);
conn.Open();
string insert = "insert into Transactions (Destination) values (#Destination)";
SqlCommand com = new SqlCommand(insert, conn);
com.Parameters.AddWithValue("#Destination", DropDownList1.SelectedItem);
com.ExecuteNonQuery();
conn.Close();
}
catch (Exception ex)
{
Response.Write("Error: " + ex.ToString());
}
Response.Redirect("Booking.aspx");
}
On the next page I have relatively the same code to enter the amount of passengers:
protected void Button2_Click(object sender, EventArgs e)
{
try
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DatabaseConnectionString"].ConnectionString);
conn.Open();
string insert = "insert into Transactions (Passengers) values (#Passengers)";
SqlCommand com = new SqlCommand(insert, conn);
com.Parameters.AddWithValue("#Passengers", DropDownList1.SelectedItem);
com.ExecuteNonQuery();
conn.Close();
}
catch(Exception ex)
{
Response.Write("Error: " + ex.ToString());
}
Response.Redirect("Payment.aspx");
}
But after doing this no data gets entered into the database. If anyone knows of anyway that I can enter data into the database one piece at a time please let me know.
If it can’t be done this way and there is a much better way of doing this again please let me know.
Thank you all for your time.
You should have a dedicated primary key column on your table, I recommend an autoincrementing integer.
CREATE TABLE [dbo].[Transactions]
(
[ID] INT NOT NULL PRIMARY KEY IDENTITY(1,1),
[CardNumber] NCHAR (10) NULL,
[Cost] NCHAR (10) NULL,
[Passengers] NCHAR (10) NULL,
[Destination] NCHAR (10) NULL
);
Then, use stored procedures, not ad-hoc SQL.
CREATE PROCEDURE TransactionSave
(
#ID int = null,
#CardNumber nchar(10) = null,
#Cost nchar(10) = null,
#Passengers nchar(10) = null,
#Destination nchar(10) = null
)
AS
BEGIN
DECLARE #ExistingID int
SELECT #ExistingID = ID FROM Transaction WHERE ID = #ID
IF #ExistingID is null
BEGIN
--Insert
INSERT INTO Transaction (CardNumber, Cost, Passengers, Destination)
VALUES (#CardNumber, #Cost, #Passengers, #Destination)
SELECT CAST(SCOPE_IDENTITY() AS INT) AS 'TransactionID'
END
ELSE
BEGIN
--Update
UPDATE Transaction
SET
CardNumber = ISNULL(#CardNumber, CardNumber),
Cost = ISNULL(#Cost, Cost),
Passengers = ISNULL(#Passengers, Passengers),
Destination = ISNULL(#Destination, Destination),
WHERE ID = #ExistingID
SELECT #ExistingID AS 'TransactionID'
END
END
Then, in your code behind, you need to retain the ID value of the Transaction you are working on, to be sure you're updating the proper row:
protected void Button2_Click(object sender, EventArgs e)
{
int transactionID = hfID.Value;
try
{
using(SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DatabaseConnectionString"].ConnectionString))
{
SqlCommand cmd = new SqlCommand("TransactionSave", conn);
cmd.Parameters.AddWithValue("#ID", transactionID);
cmd.Parameters.AddWithValue("#Passengers", DropDownList1.SelectedValue);
transactionID = cmd.ExecuteScalar();
hfID.Value = transactionID;
}
}
catch(Exception ex)
{
Response.Write("Error: " + ex.ToString());
}
}