I am trying to send a comma delimited string of dates to my stored procedure, to be used in an IN clause.
However, I am getting the error "Conversion failed when converting date and/or time from character string."
I am trying to use these dates to find prices that match.
C#:
StringBuilder LastDaysOfEveryMonth = new StringBuilder();
DataAccess da = new DataAccess();
SqlCommand cm = new SqlCommand();
cm.CommandType = CommandType.StoredProcedure;
var pList = new SqlParameter("#DateList", DbType.String);
pList.Value = LastDaysOfEveryMonth.ToString();
cm.Parameters.Add(pList);
...
cm.CommandText = "spCalculateRollingAverage";
DataSet ds = da.ExecuteDataSet(ref cm);
When debugging it, the value of the passed string is :
'2013-07-31','2013-08-30','2013-09-30','2013-10-31','2013-11-29','2013-12-31',
'2014-01-31','2014-02-28','2014-03-31','2014-04-03',
with the DbType String and SQLDbType NvarChar.
Any advice would be much appreciated!
SQL:
CREATE PROCEDURE [dbo].[spCalculateRollingAverage]
#StartDate DateTime,
#EndDate DateTime,
#Commodity nvarchar(10),
#PeakType nvarchar (10),
#BaseID int,
#NumberOfMonths int,
#DateList nvarchar(MAX)
AS
BEGIN
select TermDescription,ContractDate,Price,SortOrder into #tbtp from BaseTermPrice
inner hash join Term
on
Term.TermID = BaseTermPrice.TermID
where
BaseID = #BaseID and ((#PeakType IS NULL and PeakType is null) or
(#PeakType IS NOT NULL and PeakType=#PeakType))
and ((#DateList IS NULL and ContractDate between #StartDate and #EndDate)
or (#StartDate IS NULL and ContractDate in (#DateList)))
order by
ContractDate,SortOrder
You cannot use a varchar variable in an IN clause like this. You have to either add it to a dynamic SQL to execute, or - split it into temp table/temp variable.
For example using this SplitString function you can do something like this:
or (#StartDate IS NULL and ContractDate in
(SELECT Name from dbo.SplitString(#DateList))))
Related
I'm writing WinForms code in C# and basically have the following:
DateTime localDate = DateTime.UtcNow;
SqlConnection Conn = new SqlConnection("....[connection info]....");
Conn.Open();
SqlCommand sqlcomm = new SqlCommand("INSERT INTO dbo.Call VALUES(...., #Time,...);", Conn);
sqlcomm.Parameters.Add("#Time", SqlDbType.DateTime);
sqlcomm.Parameters["#Time"].Value = localDate;
Int32 o = sqlcomm.ExecuteNonQuery();
This throws an error of "When converting a string to DateTime, parse the string to take the date before putting each variable into the DateTime object." From what I gather, it thinks the localDate variable is a string but if I write to the console localDate.GetType() it says System.DateTime.
The column for "#Time" in the database is set up to DateTime, so that's not the issue. Any thoughts?
You're almost there. for Sql Server think among these lines
select cast(getdate() as time)
Problem is that in .net there is no such type as time, so you need to adapt
SqlCommand sqlcomm = new SqlCommand(
"INSERT INTO dbo.Call VALUES(...., cast(#Time as time),...);", Conn);
sqlcomm.Parameters.AddWithValue("#Time", localDate);
this is all you should need. Although, I think, you may not even need to add cast as DB engine itself will try cast it. The problem, I think, that you explicitly said your type SqlDbType.DateTime. But if you use AddWithValue, provider will do things for you.
since #Frédéric mentioned TimeSpan, you can try this as well
sqlcomm.Parameters.AddWithValue("#Time", localDate.TimeOfDay);
' and no cast(#Time as time)
The UTC format is not an sql date time format whuch you specified in the parameters.add
In the past I have formatted the utc time to that of yyyy-MM-dd.
Given this table schema:
create table dbo.call_history
(
id int not null identity(1,1) primary key clustered ,
my_date date null ,
my_time time null ,
my_datetime datetime null ,
)
This code works just fine:
using ( SqlConnection conn = new SqlConnection( connectString ) )
using ( SqlCommand cmd = conn.CreateCommand() )
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = #"
insert dbo.call_history ( my_date , my_time , my_datetime )
values ( #pDate , #pDate , #pDate )
select scope_identity()
";
cmd.Parameters.AddWithValue( "#pDate" , DateTime.UtcNow );
conn.Open();
// double downcast required herebecause scope_identity()
// returns numeric(38,0) which gets mapped to decimal
int id = (int)(decimal) cmd.ExecuteScalar() ;
conn.Close();
}
I have written a stored procedure
Then I have written C# code
ds.Tables[0].Rows[0][0].ToString().Split(',')
ds is my dataset which contains stored procedure retrieved from above ImportData (Column SQLQueryName))
The question is: I used string array for strMendetory and I need to find out data type of parameter of stored procedure from strMendetory. When I implement code for the same it returns System.string as usual (even it contains DATETIME).
Please give me suggestion on the same.
Stored procedure code:
CREATE PROCEDURE AddDepartments
#DepartmentName VARCHAR(100),
#DepartmentCode VARCHAR(100),
#CompanyLocationID INT,
#Details VARCHAR(100),
#Flag BIT,
#CreatedDateTime DATETIME,
#MarkDeleted BIT,
#AccessGroupMasterID INT
AS
BEGIN
INSERT INTO Departments(DepartmentName, DepartmentCode, CompanyLocationID,
Details, Flag, CreatedDateTime, MarkDeleted,
AccessGroupMasterID)
VALUES(#DepartmentName, #DepartmentCode, #CompanyLocationID,
#Details, #Flag, #CreatedDateTime, #MarkDeleted,
#AccessGroupMasterID)
END
GO
INSERT INTO ImportData(ImportItemName, ReferenceTableName, ImportFileName,
SQLQueryTypeID, SQLQueryName, MarkDeleted)
VALUES('Department', 'Departments', 'ImportDepartments.xls',
2, 'AddDepartments, #DepartmentName, #DepartmentCode, #CompanyLocationID, #Details, #Flag, #CreatedDateTime, #MarkDeleted, #AccessGroupMasterID', 0)
GO
C# code:
String[] strMendetory = ds.Tables[0].Rows[0][0].ToString().Split(',');
string columnValue="MynewValuToInsertinProcedure";
for (int columCounter = 1; columCounter < strMendetory.Count(); columCounter++)
{
cmd.Parameters.AddWithValue(strMendetory[columCounter], columnValue);
}
Use sqlparameter, you can specify the sql datatype. Here's the code example :
SqlParameter[] param = {
new SqlParameter("#param1", SqlDbType.VarChar, 100),
new SqlParameter("#param2", SqlDbType.Int),
new SqlParameter("#param3", SqlDbType.DateTime)
};
//#param1, #param2, #param3 should match your parameters name in stored procedure
param[0].Value = "test"; //pass the value to your parameter here
param[1].Value = 2;
param[2].Value = DateTime.Now;
SqlCommand sqlcmd = new SqlCommand();
foreach (SqlParameter x in param)
{
sqlcmd.Parameters.Add(x);
}
I have a DateTime component in my code, and I want to use it for a query in my SQL Server database.
When inserting this component, there seems to be no problem, but when querying for smalldatetime values, I just don't know how to do it. The dataset is always empty.
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "ReadDates";
dataset = new DataSet();
SqlParameter parameter = new SqlParameter("#date", SqlDbType.SmallDateTime);
parameter.Value = DateTime.Now();
cmd.Parameters.Add(parameter);
dataAdapter = new SqlDataAdapter(cmd);
dataAdapter.Fill(dataset);
return dataset;
And this is in my stored procedure:
select * from TableDates
where ValueDate <= #date
So I have no problems running the procedure in SQL Server Management Studio, when entering a parameter in this format: '2000-03-03 04:05:01', but when passing a DateTime, the query is always empty. Any suggestions?
I tried it by using SQL Server 2008 R2 Express.
Here is the example stored procedure i wrote:
CREATE PROCEDURE [dbo].[ShowGivenSmallDateTimeValue]
#givenSmallDateTime smalldatetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Simply return the given small date time value back to sender.
SELECT #givenSmallDateTime
END
And here is the C# code to execute the procedure:
var connectionBuilder = new SqlConnectionStringBuilder();
connectionBuilder.DataSource = "localhost\\sqlexpress";
connectionBuilder.IntegratedSecurity = true;
var now = DateTime.UtcNow;
using (var connection = new SqlConnection(connectionBuilder.ConnectionString))
using (var command = new SqlCommand())
{
command.Connection = connection;
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "ShowGivenSmallDateTimeValue";
command.Parameters.Add(new SqlParameter("#givenSmallDateTime", SqlDbType.SmallDateTime) { Value = now });
connection.Open();
var result = (DateTime)command.ExecuteScalar();
var difference = result - now;
Console.WriteLine("Due to the smalldatetime roundings we have a difference of " + difference + ".");
}
And it simply works.
Here's my code for creating the SqlParameter for a Datetime; For SQL Server 2008 we pass the value as datetime2 since SQL will implicity convert from datetime2 to every other date type so long as it is within the range of the target type...
// Default conversion is now DateTime to datetime2. The ADO.Net default is to use datetime.
// This appears to be a safe change as any datetime parameter will accept a datetime2 so long as the value is within the
// range for a datetime. Hence this code is acceptable for both datetime and datetime2 parameters, whereas datetime is not
// (because it doesn't handle the full range of datetime2).
SqlParameter sqlParam = new SqlParameter(name, SqlDbType.DateTime2);
Since Your parameter includes zeros in day and month parts...sql server converts it but doest match to your date.... i.e.,
if DATETIME.now() returns '2000-03-03 04:05:01'... it is casted into 2000-3-3 Without including zeros...so u need to specify zeros also to match your date.
I have a simple SqlCommand in which I want to return all records within a specified DateTime range. (both Date and Time are involved)
var dataReader = new SqlCommand(
#"Select RecordID from RecordTable
where RecordTable.WorkingDT between '"+ _startDt +"' and '"+ _endDt +"'",
_sqlConnection).ExecuteReader();
how do I have to set the values for _startDt and _endDt?
You can try this:
var dataReader = new SqlCommand(
#"Select RecordID from RecordTable
where RecordTable.WorkingDT between '"+ _startDt.ToString("yyyy-MM-dd HH:mm:ss") +"' and '"+ _endDt.ToString("yyyy-MM-dd HH:mm:ss") +"'",
_sqlConnection).ExecuteReader();
Where _startDt and _endDt are type of DateTime.
If you add them as proper parameters in your command, you don't need to worry about formatting. The added benefit of getting into the habit of using these is that you don't have to worry about SQL injection when you were to supply strings as parameters.
Have a look at http://www.csharp-station.com/Tutorial/AdoDotNet/lesson06
It shows how to use parameters in your queries. I don't think it needs to be spelled out completely.
An example (copy/pasted from the site):
// 1. declare command object with parameter
SqlCommand cmd = new SqlCommand(
"select * from Customers where city = #City", conn);
// 2. define parameters used in command object
SqlParameter param = new SqlParameter();
param.ParameterName = "#City";
param.Value = inputCity;
// 3. add new parameter to command object
cmd.Parameters.Add(param);
// get data stream
reader = cmd.ExecuteReader();
And yes, defining the parameters can be done shorter that 3 lines per parameter. But that's left up to the reader.
#kmatyaszek, While commonly used, the "yyyy-mm-dd HH:mm:ss" date format is not guaranteed to be unambiguously parsed by SQL server. If you must create SQL from concatenating strings (not necessary in this case as René has shown) then you should use the ISO8601 format, which is just the same but with a T in the middle: "yyyy-mm-ddTHH:mm:ss".
http://msdn.microsoft.com/en-us/library/ms190977%28v=sql.90%29.aspx
"The advantage in using the ISO 8601 format is that it is an
international standard. Also, datetime values that are specified by
using this format are unambiguous. Also, this format is not affected
by the SET DATEFORMAT or SET LANGUAGE settings."
For a demonstration why, try this rerunnable Sql script.
if object_id('tempdb..#Foo') is not null drop table #Foo;
create table #Foo(id int, d datetime)
-- Intend dates to be 12th Jan.
set dateformat ymd
insert into #Foo(id, d) values (1, '2012-01-12 01:23:45') -- ok
insert into #Foo(id, d) values (2, '2012-01-12T01:23:45') -- ok
set dateformat ydm
insert into #Foo(id, d) values (3, '2012-01-12 01:23:45') -- wrong!
insert into #Foo(id, d) values (4, '2012-01-12T01:23:45') -- ok
select * from #Foo order by id
if object_id('tempdb..#Foo') is not null drop table #Foo;
So this is the code I have tried, in C#, which failed to give me the result I needed.
SqlCommand comm = new SqlCommand("exec sys.xp_readerrorlog 0,1,'','',#StartDate,#EndDate,N'Desc'");, conn);
comm.Parameters.AddWithValue("#StartDate", "");
comm.Parameters.AddWithValue("#EndDate", "");
SqlDataReader dr = comm.ExecuteReader();
while (dr.Read())
{
Console.WriteLine(dr.GetString(0));
}
Basically, I need to extract data from these logs (which gets pulled from the SQL Server through this stored procedure), and it seems that When I use a dataReader, there are no records, and if I use a dataset with data adapter, there are also no tables/records in the dataset. This information is critical for me to query.
Is there a way that I can still query the SQL Server error logs without having to resort to stored procedures?
ANOTHER UPDATE:
The parameters for this extended stored procedures are:
Value of error log file you want to read: 0 = current, 1 = Archive, 2 = etc...
Log file type: 1 or NULL = error log, 2 = SQL Agent log
Search string 1: String one you want to search for
Search string 2: String two you want to search for to further refine
the results
Search from start time
Search to end time
Sort order for results: N'asc' = ascending, N'desc' = descending
Another method I tried
SqlCommand comm = new SqlCommand(#"exec sys.xp_readerrorlog 0,1,'','',null,null,N'Desc'", conn);
SqlDataAdapter da = new SqlDataAdapter(comm);
DataSet ds = new DataSet();
Console.WriteLine(ds.Tables.Count); //0 returned: no data in dataset
If i was allowed to use stored procedures to query the data, I could have used this following extract, but it would have been deployed too much and be a pain to maintain and decommission
IF (EXISTS( SELECT * FROM sys.procedures where name = 'writelogs' ))
BEGIN
DROP PROCEDURE Writelogs;
END
GO
CREATE PROCEDURE WriteLogs #Servername varchar(40),#InstanceName varchar(40),#Pattern varchar(max),#ParamBeginDate varchar(40), #ParamEndDate varchar(40) AS
BEGIN
DECLARE #BeginDate DateTime
DECLARE #EndDate DateTime
DECLARE #NextQueryID int
--First we have to convert the timestamps EndDate and BeginDate to something usable
IF (#ParamBeginDate = 'Beginning')
BEGIN
SET #BeginDate = null; --null will cause sys.xp_readerrorlog to read from beginning
END
ELSE IF (#ParamBeginDate = 'Last')
BEGIN
SELECT TOP 1 #BeginDate = L.TimeLogged FROM LogTable L ORDER BY L.TimeLogged Desc
END
ELSE
BEGIN
BEGIN TRY
SET #BeginDate = CAST(#ParamBeginDate AS DATETIME);
END TRY
BEGIN CATCH
SET #BeginDate = null;
END CATCH
END
IF (#ParamEndDate = 'Now')
BEGIN
SET #EndDate = GETDATE(); --null will cause sys.xp_readerrorlog to read till now
END
ELSE
BEGIN
BEGIN TRY
SET #EndDate = CAST(#ParamEndDate AS DATETIME);
END TRY
BEGIN CATCH
SET #EndDate = GETDATE();
END CATCH
END
--Temporary Table to store the logs in the format it is originally written in
CREATE TABLE TMP
(LogDate DateTime2
,Processinfo varchar(40)
,[Text] varchar(max))
--truncate the milliseconds (else ALL records will be retrieved)
SET #EndDate= dateadd(millisecond, -datepart(millisecond, #EndDate),#EndDate);
SET #BeginDate= dateadd(millisecond, -datepart(millisecond, #BeginDate),#BeginDate);
INSERT INTO TMP exec sys.xp_readerrorlog 0,1,'','',#BeginDate,#EndDate,N'DESC';
SELECT TOP 1 L.TimeLogged FROM LogTable L ORDER BY L.Timelogged desc
INSERT INTO LogTable
SELECT #Servername,#InstanceName,T.[text],T.LogDate,GETDATE(),0,0,null,#NextQueryID FROM TMP t WHERE PATINDEX(#Pattern,t.[Text]) > 0;
DROP TABLE TMP;
END
You can't use AddWithValue for the dates.
If the dates are blank, then you need to pass null as the value, not an empty string. Those have completely different meanings.
To test, open Management Studio and execute the following:
exec sys.xp_readerrorlog 0,1, '', '', '', ''
That will have zero results. However if you do this:
exec sys.xp_readerrorlog 0,1, '', '', null, null
You will get back a lot of records.
BTW, your update is still wrong. The dataset code you have will never do anything. Change it to:
SqlCommand comm = new SqlCommand(#"exec sys.xp_readerrorlog 0,1,'','',null,null,N'Desc'", conn);
SqlDataAdapter da = new SqlDataAdapter(comm);
DataSet ds = new DataSet();
da.Fill(ds, "sometablename");
Console.WriteLine(ds.Tables.Count); //0 returned: no data in dataset
Note the fill command...