This seems pretty trivial, but it is now frustrating me.
I am using C# with SQL Server 2005 Express.
I am using the following code. I want to check if a database exists before creating it. However, the integer returned is -1 and this is how MSDN defines what ExecuteNonQuery() will return as well. Right now, the database does exist but it still returns -1. Having said that, how can I make this work to get the desired result?
private static void checkInventoryDatabaseExists(ref SqlConnection tmpConn, ref bool databaseExists)
{
string sqlCreateDBQuery;
try
{
tmpConn = new SqlConnection("server=(local)\\SQLEXPRESS;Trusted_Connection=yes");
sqlCreateDBQuery = "SELECT * FROM master.dbo.sysdatabases where name =
\'INVENTORY\'";
using (tmpConn)
{
tmpConn.Open();
tmpConn.ChangeDatabase("master");
using (SqlCommand sqlCmd = new SqlCommand(sqlCreateDBQuery, tmpConn))
{
int exists = sqlCmd.ExecuteNonQuery();
if (exists <= 0)
databaseExists = false;
else
databaseExists = true;
}
}
}
catch (Exception ex) { }
}
As of SQL Server 2005, the old-style sysobjects and sysdatabases and those catalog views have been deprecated. Do this instead - use the sys. schema - views like sys.databases
private static bool CheckDatabaseExists(SqlConnection tmpConn, string databaseName)
{
string sqlCreateDBQuery;
bool result = false;
try
{
tmpConn = new SqlConnection("server=(local)\\SQLEXPRESS;Trusted_Connection=yes");
sqlCreateDBQuery = string.Format("SELECT database_id FROM sys.databases WHERE Name
= '{0}'", databaseName);
using (tmpConn)
{
using (SqlCommand sqlCmd = new SqlCommand(sqlCreateDBQuery, tmpConn))
{
tmpConn.Open();
object resultObj = sqlCmd.ExecuteScalar();
int databaseID = 0;
if (resultObj != null)
{
int.TryParse(resultObj.ToString(), out databaseID);
}
tmpConn.Close();
result = (databaseID > 0);
}
}
}
catch (Exception ex)
{
result = false;
}
return result;
}
This will work with any database name you pass in as a parameter, and it will return a bool true = database exists, false = database does not exist (or error happened).
Reading this a few years on and there's a cleaner way of expressing this:
public static bool CheckDatabaseExists(string connectionString, string databaseName)
{
using (var connection = new SqlConnection(connectionString))
{
using (var command = new SqlCommand($"SELECT db_id('{databaseName}')", connection))
{
connection.Open();
return (command.ExecuteScalar() != DBNull.Value);
}
}
}
shouldn't this
"SELECT * FROM master.dbo.sysdatabases where name = \'INVENTORY\'"
be this?
"SELECT * FROM master.dbo.sysdatabases where name = 'INVENTORY'"
Also According to MSDN
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.
You are doing a SELECT not an DML statement. Why don't you use a ExecuteReader method instead?
An alternative to querying the system views is to use the function db_id which returns the Id of the database if it exists, otherwise null. Example T-SQL below:
if (db_id('INVENTORY') is null)
begin
return 0
end
else
begin
return 1
end
Took Stephen Lloyd's code and added some async and sql injection mitigation.
public static async Task<bool> TestDatabase(string connectionString, string databaseName)
{
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand("SELECT db_id(#databaseName)", connection))
{
command.Parameters.Add(new SqlParameter("databaseName", databaseName));
connection.Open();
return (await command.ExecuteScalarAsync() != DBNull.Value);
}
}
You can't use ExecuteNonQuery because it will always return -1 for SELECT, as the MSDN link shows.
You'll have to use process a resultset eg SELECT DB_ID('INVENTORY') AS DatabaseID or use a variable/parameter: SELECT #DatabaseID = DB_ID('INVENTORY')
For the benefit of searchers, if you are using Entity Framework, this will work:
using (var ctx = new MyDataModel())
{
dbExists = System.Data.Entity.Database.Exists(ctx.Database.Connection);
}
Use this Assembly: Microsoft.SqlServer.SqlManagementObjects => NuGet
using Microsoft.SqlServer.Management.Smo;
var dbExists = new Server(serverOrInstanceName).Databases.Contains(dataBaseName);
Related
I have a very silly problem. I am doing a select, and I want that when the value comes null, return an empty string. When there is value in sql query, the query occurs all ok, but if there is nothing in the query, I have to give a sqlCommand.CommandTimeout greater than 300, and yet sometimes gives timeout. Have a solution for this?
public string TesteMetodo(string codPess)
{
var vp = new Classe.validaPessoa();
string _connection = vp.conString();
string query = String.Format("SELECT COUNT(*) FROM teste cliente WHERE cod_pess = {0}", codPess);
try
{
using (var conn = new SqlConnection(_connection))
{
conn.Open();
using (var cmd = new SqlCommand(query, conn))
{
SqlDataReader dr = cmd.ExecuteReader();
if(dr.HasRows)
return "";
return codPess;
}
}
}
You should probably validate in the UI and pass an integer.
You can combine the usings to a single block. A bit easier to read with fewer indents.
Always use parameters to make the query easier to write and avoid Sql Injection. I had to guess at the SqlDbType so, check your database for the actual type.
Don't open the connection until directly before the .Execute. Since you are only retrieving a single value you can use .ExecuteScalar. .ExecuteScalar returns an Object so must be converted to int.
public string TesteMetodo(string codPess)
{
int codPessNum = 0;
if (!Int32.TryParse(codPess, out codPessNum))
return "codPess is not a number";
var vp = new Classe.validaPessoa();
try
{
using (var conn = new SqlConnection(vp.conString))
using (var cmd = new SqlCommand("SELECT COUNT(*) FROM teste cliente WHERE cod_pess = #cod_pess", conn))
{
cmd.Parameters.Add("#cod_pess", SqlDbType.Int).Value = codPessNum;
conn.Open();
int count = (int)cmd.ExecuteScalar();
if (count > 0)
return "";
return codPess;
}
}
catch (Exception ex)
{
return ex.Message;
}
}
First, I did search for this first and found this question answered: Previous Answer
The problem I have with this answer is that using this method causes a NullReferenceException. Obviously the code will still work with a try/catch block, but I had always understood that intentionally using an Exception as a means of controlling flow was poor design. Is there a better method of doing this?
To be clear as to the parameters, I am using OleDbConnection and OleDbCommand to read/write an Access 2010 database (.accdb format).
Below is the code I have currently using the above answer's approach:
public bool ReadAccessDb(string filePath, string queryString, bool hasRecords)
{
string connectionString = #"Provider=Microsoft.ACE.OLEDB.12.0;" +
#"Data Source=" + filePath + ";" +
#"User Id=;Password=;";
using (OleDbConnection connection = new OleDbConnection(connectionString))
using (OleDbCommand command = new OleDbCommand(queryString, connection))
{
try
{
connection.Open();
int count = (int)command.ExecuteScalar();
//This works, but if no records exist it uses the Exception system to halt further action. Look for better approach.
if(count > 0)
{
hasRecords = true;
}
}
catch (System.Exception ex)
{
}
}
return hasRecords;
}
You can use :
int count = command.ExecuteScalar() is DBNull ? 0 : Convert.ToInt32(command.ExecuteScalar());
or use :
object obj = command.ExecuteScalar();
int count = obj is DBNull ? 0 : Convert.ToInt32(obj);
I'm posting this answer because the question is a bit ambiguous and the (currently) accepted answer can conceivably be "fooled" if a row exists but the first column returned by the SELECT statement happens to contain a NULL. Consider the test table
CREATE TABLE MyTable (ID COUNTER PRIMARY KEY, NullableColumn INTEGER NULL)
INSERT INTO MyTable (NullableColumn) VALUES (NULL)
which would look like
ID NullableColumn
-- --------------
1 <NULL>
If the SELECT statement used for testing was
string sql = "SELECT NullableColumn FROM MyTable WHERE ID=1";
then the method
static bool RecordExists(string queryString, OleDbConnection conn)
{
bool rtn;
using (var command = new OleDbCommand(queryString, conn))
{
object obj = command.ExecuteScalar();
int count = obj is DBNull ? 0 : Convert.ToInt32(obj);
rtn = (count != 0);
}
return rtn;
}
would return False, whereas this method
static bool RecordExists(string queryString, OleDbConnection conn)
{
bool rtn;
string rowCountString = String.Format("SELECT COUNT(*) AS n FROM ({0})", queryString);
using (var command = new OleDbCommand(rowCountString, conn))
{
int count = (int)command.ExecuteScalar();
rtn = (count != 0);
}
return rtn;
}
would return True.
-edit- wrote a clearer explanation of what's not working down bottom. maybe just skip to that.
I'm having an issue with a stored procedure I wrote and getting the result in my c# application, yet when I execute it within MySQL benchmark the result returns fine.
Firstly the procedure is:
CREATE DEFINER=`root`#`localhost` PROCEDURE `GetNextOpponent`(IN p_user_id INT, OUT p_target_id INT, OUT p_target_data MEDIUMBLOB, OUT p_target_rank INT)
BEGIN
DECLARE UserRank INT;
CALL DeleteOldSearches(); /* TODO remove and call on interval instead of every time*/
SET UserRank = (SELECT rank FROM world WHERE user_id = p_user_id);
IF UserRank IS NOT NULL
THEN
SELECT user_id, world_data, rank
INTO p_target_id, p_target_data, p_target_rank
FROM world
WHERE
user_id != p_user_id AND
user_id NOT IN (SELECT target_id FROM searches WHERE user_id = p_user_id) AND
shield < CURRENT_TIMESTAMP
ORDER BY ABS(UserRank - 3)
LIMIT 1;
END IF;
IF p_target_id IS NOT NULL
THEN
INSERT INTO searches (user_id, target_id)
VALUES (p_user_id, p_target_id);
END IF;
/*SELECT TargetID, TargetData, TargetRank;*/
END
Now if I call it in WorkBench with
call battlecraft_test.GetNextOpponent(1, #p_target_id, #p_target_data, #p_target_rank);
select #p_target_id, #p_target_data, #p_target_rank;
I have no issue, get a nice result
'3', BLOB, '2'
However if I execute it in my app with,
public static bool GetNextOpponent(int userID)
{
MySqlConnection conn = null;
try
{
conn = new MySqlConnection(ConnectionString);
conn.Open();
using (var cmd = new MySqlCommand("GetNextOpponent", conn) {CommandType = CommandType.StoredProcedure})
{
cmd.Parameters.Add("#p_user_id", MySqlDbType.Int32).Value = userID;
cmd.Parameters.Add("#p_target_id", MySqlDbType.Int32).Direction = ParameterDirection.Output;
cmd.Parameters.Add("#p_target_data", MySqlDbType.MediumBlob).Direction = ParameterDirection.Output;
cmd.Parameters.Add("#p_target_rank", MySqlDbType.Int32).Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
object a = cmd.Parameters["#p_target_id"].Value; // null
object b = cmd.Parameters["#p_target_data"].Value; // null
object c = cmd.Parameters["#p_target_rank"].Value; // null =(
return true;
}
}
catch (Exception ex)
{
Logger.LogError($"Unexpected exception of type {ex.GetType()}: {ex.Message}");
return false;
}
finally
{
conn?.Close();
}
}
The out params are all null.
There are a few interesting cases where I've got results out from my application, for instance most times if I step through each line of code while debugging it works fine, however most of the time nothing happens.
I'm really struggling with this, spent hours on it now - my database knowledge isn't as good as I'd like it to be at the moment and I'm out of ideas, so I'm hoping someone has an idea what it could be.
One of my attempts to fix this I tried not using OUT keywords and instead just returning the field and using ExecuteReader instead yet it still only sometimes works.
If I write another procedure to kind of wrap this procedure like so:
CREATE DEFINER=`root`#`localhost` PROCEDURE `test`(IN p_user_id INT)
BEGIN
call battlecraft_test.GetNextOpponent(p_user_id, #p_target_id, #p_target_data, #p_target_rank);
select #p_target_id, #p_target_data, #p_target_rank;
END
It works when I execute it using Reader, however the first row is always null and the second row has the result. It's a usable workaround but I'd rather get to the cause of it.
Thanks for any response in advance.
-edit-
Even stripping it back I have issues. If I reduce stored procedure to just
CREATE DEFINER=`root`#`localhost` PROCEDURE `GetNextOpponent`(IN p_user_id INT)
BEGIN
SELECT user_id, world_data, rank
FROM world
WHERE
user_id != p_user_id AND
user_id NOT IN (SELECT target_id FROM searches WHERE user_id = p_user_id) AND
shield < CURRENT_TIMESTAMP
ORDER BY ABS((SELECT rank FROM world WHERE user_id = p_user_id) - rank)
LIMIT 1;
END
And then do it as a reader
public static bool GetNextOpponent(int userID)
{
MySqlConnection conn = null;
try
{
conn = new MySqlConnection(ConnectionString);
conn.Open();
using (var cmd = new MySqlCommand("GetNextOpponent", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#p_user_id", MySqlDbType.Int32).Value = userID;
//System.Threading.Thread.Sleep(1000); // Un-commenting this makes it work...
using (var rdr = cmd.ExecuteReader())
{
if (!rdr.Read())
return false; // returns here
var r1 = rdr.GetValue(0); // null
var r2 = rdr.GetValue(1); // null
var r3 = rdr.GetValue(2); // null
return true;
}
}
}
catch (Exception ex)
{
Logger.LogError($"Unexpected exception of type {ex.GetType()}: {ex.Message}");
return false;
}
finally
{
conn?.Close();
}
}
While executing it in workbench still returns a nice result
call battlecraft_test.GetNextOpponent(1);
There are many things to be improved in your code
In the finally block of your try/catch validate and close the connection
Dont use generic Catch as the first level of catch use ("DatabaseSpecificExceptions" or "RunTimeExceptions" or any other Expected expection" and log that to a file or by email to debug furthers
public static bool GetNextOpponent(int userID)
{
MySqlConnection conn = null;
boolean resp ;
//we assume that if fails
resp = false ;
try
{
conn = new MySqlConnection(ConnectionString);
if(conn != null)
{
conn.Open();
using (var cmd = new MySqlCommand("GetNextOpponent", conn) {CommandType = CommandType.StoredProcedure})
{
//Addding parameters to the SQL commands - good comments never hurt
cmd.Parameters.Add("#p_user_id", MySqlDbType.Int32).Value = userID;
cmd.Parameters.Add("#p_target_id", MySqlDbType.Int32).Direction = ParameterDirection.Output;
cmd.Parameters.Add("#p_target_data", MySqlDbType.MediumBlob).Direction = ParameterDirection.Output;
cmd.Parameters.Add("#p_target_rank", MySqlDbType.Int32).Direction = ParameterDirection.Output;
//you are executing a NonQuery but expectin results ??
cmd.ExecuteNonQuery();
//see comments above
object a = cmd.Parameters["#p_target_id"].Value; // null
object b = cmd.Parameters["#p_target_data"].Value; // null
object c = cmd.Parameters["#p_target_rank"].Value; // null =(
//avoid returns in the middle of the flow until debug works
//return true;
resp = true ;
}
}
}
catch (Exception ex)
{
Logger.LogError($"Unexpected exception of type {ex.GetType()}: {ex.Message}");
}
finally
{
if(conn != null)
{
conn?.Close();
}
}
}
I'm trying to develop a little app that will run a CREATE PROCEDURE script. A simple example:
IF EXISTS (SELECT * FROM sysobjects WHERE type = 'P' AND name = 'ap_get_roles_in_system')
BEGIN
DROP Procedure [dbo].[ap_get_roles_in_system]
END
GO
CREATE PROCEDURE [dbo].[ap_get_roles_in_system]
(
#system_id int
)
AS
SELECT * FROM roles
GO
GRANT EXEC ON [dbo].[ap_get_roles_in_system] TO PUBLIC
GO
If I load this text up in a string, and run it by way of ExecuteNonQuery(), the first item, which drops the stored procedure, works fine, but instead of running the Create it finds a syntax error with the parameter of the stored procedure, namely: it hasn't been declared.
In short, instead of trying to run the CREATE, it is somehow trying to run the script as a script, not a CREATE. Not sure what the correct wording would be.
The script above works great if pasted into Sql Management Studio.
Here's the code I am executing:
public string RunSql(string Sql)
{
string result = string.Empty;
_conn.Open();
SqlCommand cmd = new SqlCommand(Sql, _conn);
cmd.CommandType = CommandType.Text;
try
{
cmd.ExecuteNonQuery();
result = "Succeeded";
}
catch (SqlException ex)
{
result = ex.Message;
}
return result;
}
#RichardSchneider's answer led me to the solution I found, but I thought at this late date, since there have been so many views, that I should post the code that solved the problem, which Richard's answer led me to. Here it is, an entire class named SqlAction. Note that I split the entire text with "GO", and then put the components into an array, executing each component in turn:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
namespace SProcRunner
{
public class SqlAction
{
public SqlAction(string connString)
{
SqlConnectionStringBuilder sb = new SqlConnectionStringBuilder(connString);
_conn = new SqlConnection(sb.ToString());
}
private SqlConnection _conn;
public string RunSql(string Sql)
{
string result = string.Empty;
// split the sql by "GO"
string[] commandText = Sql.Split(new string[] { String.Format("{0}GO{0}", Environment.NewLine) }, StringSplitOptions.RemoveEmptyEntries);
_conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = _conn;
cmd.CommandType = CommandType.Text;
for (int x = 0; x < commandText.Length; x++)
{
if (commandText[x].Trim().Length > 0)
{
cmd.CommandText = commandText[x];
try
{
cmd.ExecuteNonQuery();
result = "Command(s) completed successfully.";
}
catch (SqlException ex)
{
result = String.Format("Failed: {0}", ex.Message);
break;
}
}
}
if (_conn.State != ConnectionState.Closed) _conn.Close();
return result;
}
}
}
Remove the "GO" lines from the SQL.
I am trying to update a single value into my access .accdb database via a c# winform interface. my SQL statement is:
updateString("UPDATE Password_Table SET Password = '" + confirmnewpasswordTextBox.Text + "' WHERE Password_ID = 'user'");
field-wise it should be correct but whenever i execute the updateString function it only returns zero. may I know what I am doing wrongly in the following example?
public static bool updateString(string SQL)
{
using (var connection = new OleDbConnection(connectionString))
using (var command = connection.CreateCommand())
{
connection.Open();
command.CommandText = SQL;
command.CommandType = CommandType.Text;
try
{
return command.ExecuteNonQuery();
}
catch
{
return -1;//for error
}
}
}
Thank you!!
update:
System.Data.OleDb.OleDbException: Syntax error in UPDATE statement.
hmmm, i still cant figure out what is wrong, my table is Password_Table, and I am trying to update a column called Password where the Password_ID is "user".
update: found the error! turns out that Password is like a restricted keyword and i had to cover it in [ ] before it could work..
There are serious issues with your code. It is vulnerable to SQL injection. You should always use parametrized queries to avoid that. For example:
public static string UpdatePassword(string user, string password)
{
using (var connection = new OleDbConnection(connectionString))
using (var command = connection.CreateCommand())
{
connection.Open();
command.CommandText = "UPDATE Password_Table SET Password = #pwd WHERE Password_ID = #user";
command.Parameters.AddWithValue("#pwd", password);
command.Parameters.AddWithValue("#user", user);
try
{
return command.ExecuteNonQuery();
}
catch
{
return -1;//for error
}
}
}
And then invoke like this:
int rowsAffetected = UpdatePassword("user", confirmnewpasswordTextBox.Text);
Now, if this returns 0 it means that there is no record in your database which matches the Password_ID = user condition and there is nothing to update.