Sending datetime value to a stored procedure - sql

I want to send just date to a stored procedure and I wrote this C# code:
string[] pr = { "/" };
string[] s = txtStartDate.Text.Split(pr, StringSplitOptions.None);
term.Start_date = new DateTime(Convert.ToInt32(s[0]), Convert.ToInt32(s[1]), Convert.ToInt32(s[2])).Date;
s = txtEndDate.Text.Split(pr, StringSplitOptions.None);
term.End_date = new DateTime(Convert.ToInt32(s[0]),Convert.ToInt32(s[1]),Convert.ToInt32(s[2])).Date;
and I send it to the stored procedure like this:
public bool AddNewTerm(Term term)
{
SqlParameter[] parameters = new SqlParameter[]
{
new SqlParameter ("#termName",term.TermName),
new SqlParameter ("#start_date",term.Start_date),
new SqlParameter ("#end_date",term.End_date)
};
return SqlDBHelper.ExecuteNonQuery("AddNewTerm", CommandType.StoredProcedure, parameters);
}
but when it goes to the stored procedure say this:
SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.
I see other topic but they cant help me
This is the stored procedure code:
ALTER PROCEDURE dbo.AddNewTerm
(
#termName varchar(50),
#start_date date,
#end_date date
)
AS
insert into term(termName, start_date, end_date)
values(#termName, #start_date, #end_date)
RETURN
Where is the problem?

I find my answer by changing my way
I used PersianCalendar Class and store it varchar.

The error message talks about a lower limit of 1/1/1753, so, supposing that you have parsed correctly your inputs (30/6/1390) the message seems clearly indicate that the two column start_date and end_dateare of type datetimethat has a lower limit of 1/1/1753.
So, to store a date with year less than 1753 you need a datetime2 or date column that have a lower limit of 1/1/0001
Here a quick reference for the two datatypes
There is another problem in your code. You add the parameters to the array without specyfing their SqlDbType and in this way the Date are added as DateTime parameters that of course cannot accept a value below 1/1/1753. A workaround for your specific code could be.
public bool AddNewTerm(Term term)
{
List<SqlParameter> parameters = new List<SqlParameter>()
{
new SqlParameter("#termName",SqlDBType.VarChar, 50) {Value = term.TermName},
new SqlParameter("#start_date",SqlDBType.DateTime2, 0) {Value = term.Start_Date},
new SqlParameter("#end_date",SqlDBType.DateTime2, 0) {Value = term.End_Date},
};
return SqlDBHelper.ExecuteNonQuery("AddNewTerm", CommandType.StoredProcedure, parameters.ToArray());
}

Related

System.Data.SqlClient.SqlException: Incorrect syntax near '2017-03-21'

protected void btnBeds_click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["DatabaseConnectionString"].ConnectionString);
con.Open();
String checkBeds = "SELECT Count (*) FROM Bed WHERE bedID NOT IN (SELECT DISTINCT(bedID) FROM Booking where startDate>='"+TxtArrivalDate.Text+"' and endDate<= '"+txtDepartureDate.Text+"'";
SqlCommand showcheckBeds = new SqlCommand(checkBeds, con);
ResultLabel.Text = showcheckBeds.ExecuteScalar().ToString();
con.Close();
}
I'm trying to display the amount of free beds there are in the database and im getting this error.
Always use parameters in your queries
Always wrap connections and other types that implement IDisposable in using statements to ensure the resource is released
Use the correct types in your database and match that type with the passed in parameter. Example: do not pass in a string for a date, do not store dates as strings.
Your actual problem was a missing ) at the end of your sql statement as pointed out by #Damien_The_Unbeliever
Updated code with changes:
const string checkBeds = "SELECT Count (*) FROM Bed WHERE bedID NOT IN (SELECT DISTINCT(bedID) FROM Booking where startDate >= #startDate and endDate<= #endDate)";
using(SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["DatabaseConnectionString"].ConnectionString))
using(SqlCommand showcheckBeds = new SqlCommand(checkBeds, con))
{
showcheckBeds.Parameters.Add(new SqlParameter("#startDate", SqlDbType.DateTime){Value = DateTime.Parse(TxtArrivalDate.Text) });
showcheckBeds.Parameters.Add(new SqlParameter("#endDate", SqlDbType.DateTime){Value = DateTime.Parse(txtDepartureDate.Text) });
con.Open();
ResultLabel.Text = showcheckBeds.ExecuteScalar().ToString();
}
Note: In the code above I used a direct DateTime.Parse to get an actual DateTime instance to pass as a parameter. It would probably be advisable to change that either to ParseExact or to provide a CultureInfo instance to the method.
Try This
String checkBeds = "SELECT Count (*) FROM Bed WHERE bedID NOT IN (SELECT DISTINCT(bedID) FROM Booking where startDate>='"+Convert.ToDateTime( TxtArrivalDate.Text).ToStrin("dd MMM yyyy")+"' and endDate<= '"+Convert.ToDateTime(txtDepartureDate.Text).ToString("dd MMM yyyy")+"'";

Error Date in SQL Query

I have a problem with my SQL query.
The error is :
The conversion of a varchar data type to a smalldatetime data type resulted in an" + " out-of-range value.
I tried to use the CONVERT function to remedy it but in vain.
public static List<string> Helper_Statistic_6(DateTime start, DateTime end) {
DateTime dateStart = start;
DateTime dateEnd = end;
string query = "SELECT ... FROM ... WHERE DATE BETWEEN CONVERT(VARCHAR(10),'" + dateStart+ "',120) and CONVERT(VARCHAR(10),'" + dateEnd+ "',120) ";
}
I suspect you're using C# with Microsoft SQL Server.
In any case, to avoid the woes of code injection, one should try to use parametized SQL. Allow the compiler take care of marshalling a C# Date to a SQL Date.
EDIT: As per #marc_s suggestion, you should beware using reserved SQL keywords as column names, otherwise, protect them from being treated as SQL keywords by using [ ] symbols, i.e. [DATE] isntead of DATE.
I would expect the syntax to look like this:
public static void Run_Helper_Statistic_6(DateTime start, DateTime end)
{
using (SqlCommand command = new SqlCommand(
"SELECT ... FROM ... WHERE [DATE] BETWEEN #start and #end", connection))
{
command.Parameters.Add(new SqlParameter("start", start));
command.Parameters.Add(new SqlParameter("end", end));
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
// ...
}
}
}

.NET SqlParameter constructor inconsistent?

Can anyone tell me what is going on in this function??
In the following code snippet, user.Id = 0, id.Value = 0 and id.SqlDbType = Int.. as expected since user.Id is an int field.
However, error.Value = null and error.SqlDbType = BigInt. What gives? If I use non-zero it detects an int and the correct value.
Note: the Value properties are the same before and after declaring the parameter direction.
public static long InsertUpdate(User user) {
SqlParameter id = new SqlParameter("#id", user.Id);
id.Direction = ParameterDirection.InputOutput;
cmd.Parameters.Add(id);
SqlParameter error = new SqlParameter("#error_code", 0);
error.Direction = ParameterDirection.Output;
cmd.Parameters.Add(error);
.... other stuff
}
As well, if #SET #error_Code = 0 in the sproc, error.Value = NULL and error.SqlDbType = NVarChar AFTER the procedure runs. If I set it to an integer I get an Int type.
UPDATE:
After specifying SqlDbType.Int the parameter now has the correct SqlDbType before and after the command... however the stored procedure is still setting #error_code = null when I in fact set it to 0.
UPDATE:
When the sproc executes the SELECT statement the #error_code parameter is always returned as null, regardless of when or not it has been set... this only happens when there's a select statement...
Here is the procedure to reproduce:
ALTER PROCEDURE [dbo].[usp_user_insert_v1]
#username VARCHAR(255),
#password VARCHAR(255),
#gender CHAR(1),
#birthday DATETIME,
#error_code INT OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #default_dt DATETIME
EXEC #default_dt = uf_get_default_date
DECLARE #dt DATETIME = GETUTCDATE()
INSERT INTO users(username, password, gender, birthday, create_dt, last_login_dt, update_dt, deleted)
VALUES(#username, #password, #gender, #birthday, #dt, #default_dt, #default_dt, 0)
SELECT * FROM users WHERE id = SCOPE_IDENTITY()
SET #error_code = 3
RETURN
END
SOLUTION?
http://forums.asp.net/t/1208409.aspx?Interesting+problem+with+getting+OUTPUT+parameters+from+SQL+Server+using+C+
Found this link on the ASP forums... apparently you can't read the output parameter until you have read all the results from the SqlDataReader... very unfortunate for me since I decide whether or not I even WANT to read the results based on the output param...
From SqlParameter.Value on MSDN
For output and return value parameters, the value is set on completion of the SqlCommand
i.e. I wouldn't rely on type inference to set the return type implicitly.
I would explicitly set the type of the output parameter:
var error = new SqlParameter("#error_code", SqlDbType.Int)
{
Direction = ParameterDirection.Output
};
Edit
After some reflection of SqlParameter:
The BigInt is easy to explain - it is the default SqlDbType, and the SqlParameter(string parameterName, object value) ctor doesn't overwrite this value.
public enum SqlDbType
{
BigInt = 0,
...
Re: #error_code is returned as NULL
The only thing I can think of is that the PROC fails to complete cleanly. Try moving the SET #error_code = 0 above the EXEC #default_dt = uf_get_default_date ?
Edit
Confirmed, #Damien's point is correct
SqlParameter error = new SqlParameter("#error_code", 0);
Actually calls this ctor:
public SqlParameter(string parameterName, SqlDbType dbType)
whereas
SqlParameter error = new SqlParameter("#error_code", 1234);
calls
public SqlParameter(string parameterName, object value)
Reason : 0 is implicitly castable to enum.
Both of the current answers are slightly incorrect because they're based on the assumption that the constructor being called for your error object is the (string,object) one. This is not the case. A literal 0 can be converted to any enum type1, and such a conversion would be preferred over a conversion to object. So the constructor being called is the (string,SqlDbType) constructor.
So the type is set to BigInt because that's the 0 value for the SqlDbType enumeration, and the Value is null because you have no code that attempts to set the value.
SqlParameter error = new SqlParameter("#error_code", (object)0);
should cause it to select the correct overload.
Demo:
using System;
using System.Data;
namespace ConsoleApplication
{
internal class Program
{
private static void Main()
{
var a = new ABC("ignore", 0);
var b = new ABC("ignore", (object)0);
var c = new ABC("ignore", 1);
int i = 0;
var d = new ABC("ignore", i);
Console.ReadLine();
}
}
public class ABC
{
public ABC(string ignore, object value)
{
Console.WriteLine("Object");
}
public ABC(string ignore, SqlDbType value)
{
Console.WriteLine("SqlDbType");
}
}
}
Prints:
SqlDbType
Object
Object
Object
1From the C# Language specification, version 5, section 1.10 (that is, just in the introduction to the language, not buried deep down in the language lawyery bits):
In order for the default value of an enum type to be easily available, the literal 0 implicitly converts to any enum type. Thus, the following is permitted.
Color c = 0;
I'd have also thought this important enough to be in the Language Reference on MSDN but haven't found a definitive source yet.
Well, it looks like the most reliable way of doing it is by using this overload:
SqlParameter error = new SqlParameter("#error_code", SqlDBType.Int);
error.Value = 0;
The overload you're using takes an object as a parameter, and for some reason which I can't divine, it's not picking the right type.

Dapper SqlDateTime overflow using GETDATE() in SQL Server

I have the following code which gets the input from a form and calls a stored procedure to store it in a database.
[HttpPost]
public ActionResult Index(GuestMessage model)
{
if (ModelState.IsValid)
{
using(IDbConnection conn = DatabaseAcess.OpenConnection())
{
const string storedProcedure = "dbo.InsertMessage";
conn.Execute(storedProcedure, new GuestMessage { Name = model.Name, Email = model.Email, Message = model.Message }, null, null, CommandType.StoredProcedure);
return View("ThankYou");
}
}
return View();
}
There are three fields, Name, E-mail and Message that have to be filled in by the user. In the database I store the data that the user enters, from these three fields, and the time of the entry using a stored function called dbo.Insert Message.
BEGIN
BEGIN TRAN
SET NOCOUNT ON;
DECLARE #GuestID int;
INSERT INTO Guest(Name, Email) VALUES (#Name, #Email);
SELECT #GuestID = scope_identity();
INSERT INTO Message (EntryDate, GuestID, Message, Status)
VALUES (GETDATE(), #GuestID, #Message, 2);
COMMIT TRAN
END
Notice that I do not pass the entry date and time as an input parameter to the stored procedure since there is no need for that. I use the built-in function GETDATE() in SQL Server to determine the time a user has entered a message.
However, whenever I try to enter a message I get the following error:
SqlDateTime overflow. Must be between 1/1/1753 12:00:00AM and 12/31/9999 11:59:59PM
I would suggest the following change:
conn.Execute(storedProcedure,
new { Name = model.Name, Email = model.Email, Message = model.Message },
commandType: CommandType.StoredProcedure);
The only actual change there is that I have swapped new GuestMessage { ... } for new { ... }. What I suspect is happening is that your GuestMessage type has additional properties that aren't needed, but which it is sending as parameters. A DateTime left at its default value is "00:00:00.0000000, January 1, 0001". By swapping to just new { ... }, we have explicitly limited the members we are sending to Name, Email and Message, because those are the only things defined.
Note: when using plain-text, dapper does use some voodoo to try to see which parameter names are actually used in the SQL, and it doesn't send anything that it knows isn't referenced in the SQL - however, it cannot do this with a stored procedure.

How do you specify 'DEFAULT' as a SQL parameter value in ADO.NET?

I have a parameterized SQL query targetted for SQL2005 which is dynamically created in code, so I used the ADO.NET SqlParameter class to add sql parameters to SqlCommand.
In the aforementioned SQL I select from a Table Valued Function with has defaults. I want my dynamic sql to sometimes specify a value for these default parameters, and other times I want to specify that the SQL DEFAULT - as defined in the Table Valued Function - should be used.
To keep the code clean I didn't want to dynamically add the SQL DEFAULT keyword and parameterize it when a non-default is to be used, I just wanted to set DEFAULT as the value of my SQLParameter.
Can I? What is best practice in such an instance?
SQL query parameters take the place of literal values only.
You can't send an SQL keyword as the value of a parameter, just as you cannot send a table identifier, column identifier, list of values (e.g. for an IN predicate), or an expression. The value of the parameter is always interpreted as a literal value, as if you had included a quoted string literal or a numeric literal in your query.
Sorry, but you have to include an SQL keyword as part of the SQL query before you prepare that query.
AFAIK, the only way to tell SQL Server to use a default value is via the DEFAULT keyword or to exclude it from parameter list. That means that the use of the DEFAULT keyword must be in your parameterized SQL Statement. So, something like:
Select ...
From dbo.udf_Foo( DEFAULT, #Param2, #Param3, DEFAULT, .... )
I suppose another approach would be to query the system catalogs for the actual value of the various DEFAULT values and determine whether to set the SqlParameter to the default value that way, but that requires a convoluted second query to get the default values.
If you have the following function (for example):
CREATE FUNCTION dbo.UFN_SAMPLE_FUNCTION
(
#Param1 nvarchar(10),
#Param2 int = NULL
)
RETURNS TABLE
AS
RETURN
SELECT #Param1 AS Col1, #Param2 AS Col2;
GO
Then you can use it the following way (option 1):
SELECT * FROM dbo.UFN_SAMPLE_FUNCTION ('ABC', DEFAULT);
which is correct way and you get the following result:
Col1 Col2
---------- -----------
ABC NULL
But if you try to use parametrized query (option 2):
exec sp_executesql N'SELECT * FROM dbo.UFN_SAMPLE_FUNCTION (#P1, #P2)',N'#P1 nvarchar(10),#P2 int',#P1=N'abc',#P2=default;
you will get an error:
Msg 8178, Level 16, State 1, Line 0
The parameterized query '(#P1 nvarchar(10),#P2 int)SELECT * FROM dbo.UFN_SAMPLE_FUNCTION' expects the parameter '#P2', which was not supplied.
If you have the following .net code:
public void RunTVF(string param1, int? param2)
{
using (SqlConnection con = GetProdConection())
{
using (var cmd = new SqlCommand("SELECT * FROM dbo.UFN_SAMPLE_FUNCTION (#P1, #P2)", con))
{
cmd.CommandType = CommandType.Text;
var param = new SqlParameter
{
ParameterName = "#P1",
SqlDbType = SqlDbType.NVarChar,
Size = 10 ,
Value = param1
};
cmd.Parameters.Add(param);
param = new SqlParameter
{
ParameterName = "#P2",
SqlDbType = SqlDbType.Int,
Value = param2
};
cmd.Parameters.Add(param);
cmd.Connection.Open();
using (IDataReader dataReader = cmd.ExecuteReader())
{
//...
}
}
}
}
then, in case param2 = null as Jack suggested above, the script produced by the code will be identical to the option 2 and will result to the same error. So you cannot use NULL in this case.You cannot set DEFAULT as the value of SQLParameter either.
What you can do is to create a stored procedure to wrap the call to your funcion and move your default value from the function to the SP. Example:
CREATE PROCEDURE dbo.USP_SAMPLE_PROCEDURE
(
#Param1 nvarchar(10),
#Param2 int = NULL, --DEFAULT value now is here (remove it from the function)
#Statement nvarchar(max)
)
AS
BEGIN
SET NOCOUNT ON;
EXEC sp_executesql #Statement,N'#P1 nvarchar(10),#P2 int',#P1=#Param1,#P2=#Param2;
END
The .NET code will look the following way:
public void RunWrapper(string param1, int? param2)
{
using (SqlConnection con = GetProdConection())
{
using (var cmd = new SqlCommand("USP_SAMPLE_PROCEDURE", con))
{
cmd.CommandType = CommandType.StoredProcedure;
var param = new SqlParameter
{
ParameterName = "#Param1",
SqlDbType = SqlDbType.NVarChar,
Size = 10,
Value = param1
};
cmd.Parameters.Add(param);
param = new SqlParameter
{
ParameterName = "#Param2",
SqlDbType = SqlDbType.Int,
Value = param2
};
cmd.Parameters.Add(param);
param = new SqlParameter
{
ParameterName = "#Statement",
SqlDbType = SqlDbType.NVarChar,
Size = -1, //-1 used in case you need to specify nvarchar(MAX)
Value = "SELECT * FROM dbo.UFN_SAMPLE_FUNCTION (#P1, #P2)"
};
cmd.Parameters.Add(param);
cmd.Connection.Open();
using (IDataReader dataReader = cmd.ExecuteReader())
{
//...
}
}
}
}
In this case null as a value for the param2 will be translated to the correct DEFAULT and the following script will be produced:
exec USP_SAMPLE_PROCEDURE #Param1=N'ABC',#Param2=default,#Statement=N'SELECT * FROM dbo.UFN_SAMPLE_FUNCTION (#P1, #P2)'
which will give you the following result:
Col1 Col2
---------- -----------
ABC NULL
I am not sure that this is the best practice. This is just the work-around.
Though you can't set an SQL keyword as the value of a parameter, you could in this case go and get the DEFAULT VALUE.
SELECT COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'table_name' AND COLUMN_NAME = 'column_name'"
if you pass a dot net null value as the parameter value it will use sql DEFAULT
if you pass a dot net DBNull.Value it will use sql NULL