I have a stored procedure which accepts 3 parameters, can anyone advise what's the most efficient way to conditionally add to the where clause if a parameter is not null?
currently I have:
where ([LoggedIn] >= #dateFrom and [LoggedIn] <= #dateTo)
I would like to add to this if the parameter #email is not null, so conditionally doing the following
AND [Email] = #email
Thanks in advance, I hope that makes sense
There's no reason why you cannot dynamically build an SQL that has parameters in, in your code and dynamically add values for the parameters as you extend the SQL text.. it looks something like:
var sql = "SELECT * FROM Table WHERE ([LoggedIn] >= #dateFrom and [LoggedIn] <= #dateTo)";
var cmd = new SqlCommand(sql, connection);
cmd.Parameters.Add("#dateFrom", SqlDbType.DateTime).Value = dateFrom;
cmd.Parameters.Add("#dateTo", SqlDbType.DateTime).Value = dateTo;
if(email != null){
cmd.CommandText += " AND [Email] = #email";
cmd.Parameters.Add("#email", SqlDbType.VarChar, SIZE_OF_DB_COLUMN_HERE).Value = email;
}
You should, of course, choose your SqlDbType to match what is actually in your DB. If it's a datetime2(7) then it's a bit more wordy;
cmd.Parameters.Add(new SqlParameter("#paramName", SqlDbType.DateTime2) { Scale = 7, Value = someVariable });
If you're doing your DB access with Dapper, just extend the SQL text and the parameters collection in a similar way
var sql = "SELECT * FROM Table WHERE ([LoggedIn] >= #dateFrom and [LoggedIn] <= #dateTo)";
var d = new Dictionary<string, object>(){
{ "#dateFrom", dateFrom },
[ "#dateTo", dateTo }
};
if(email != null){
sql += " AND [Email] = #email";
d["#email"] = email;
}
var r = conn.Query<TypeOfObjectHere>(sql, d);
If you're doing this with EF, you can leverage the fact that you can call Where multiple times and it works like AND:
var q = context.Logins.Where(l =~> l.LoggedIn >= dateFrom && l.LoggedIn <= dateTo);
if(email != null)
q = q.Where(l => l.Email == email);
var result = q.ToList();
Related
I get this error:
Failed to convert parameter value from a String to a DateTime.
The error occurs at ExecuteDataset(Connection, cmd, query, parameters);
Code:
int paramCount = 2 + results.Count;
SqlParameter[] sqlParam = new SqlParameter[2];
sqlParam[0] = new SqlParameter("#FROMDATE", SqlDbType.DateTime);
sqlParam[0].Value = Convert.ToDateTime(SearchRequest.FromDate).ToString("yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture);
results.Add(sqlParam[0]);
sqlParam[1] = new SqlParameter("#TODATE", SqlDbType.DateTime);
sqlParam[1].Value = Convert.ToDateTime(SearchRequest.ToDate).ToString("yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture);
results.Add(sqlParam[1]);
foreach (var o in strCode)
{
SqlParameter paramRef = new SqlParameter();
paramRef.ParameterName = "#Param" + results.Count;
paramRef.Value = o;
results.Add(paramRef);
lstParam.Add(paramRef.ParameterName);
}
SqlParameter[] sqlParams = new SqlParameter[paramCount];
sqlParams = results.ToArray();
string Query;
Query = "select StudID,StudName from StudentDetails
where CourseCode in ({2}) and Coursedate between #FROMDATE and #TODATE";
var inClause = String.Join(",", lstParam);
Query = Query.Replace("{2}", inClause);
Issue is resolved, I was passing incorrect date format...
During debugging DATE do read it as e.g 20130831 which I changed it to 2013-08-31 format and it worked for me...
How do I run a raw query in ASP.NET core to return the row count from a table?
Currently, I am doing this and the returned result is -1. I think the return result is based on the number of records affected.
int numberOfRows = await
appDbContext.Database.ExecuteSqlInterpolatedAsync(
$"SELECT CODE FROM [samaster] WHERE CODE={productBrandCode} AND WAREHOUSE={warehouse} ");
Any idea on how to get the count back to numberOfRows variable will be appreciated.
NOTE: The above table is not a model so I need to run a raw query.
Thanks
It is currently not possible to get the query result when using ExecuteSqlInterpolatedAsync. The same applies to any additional LINQ Statements.
You can, however, use the underlying ADO.net Provider:
public IList<IDictionary<string, dynamic>> SelectDynamic(string table)
{
using (var command = Database.GetDbConnection().CreateCommand())
{
command.CommandText = $"SELECT * FROM [{table}]";
command.CommandType = CommandType.Text;
Database.OpenConnection();
using (var result = command.ExecuteReader())
{
var entities = new List<IDictionary<string, dynamic>>();
while (result.Read())
{
var dict = new Dictionary<string, dynamic>();
for (int i = 0; i < result.FieldCount; i++)
{
dict.Add(result.GetName(i), result.GetValue(i));
}
entities.Add(dict);
}
return entities;
}
}
}
Add this to your DbContext Class and Call it with:
using (var context = new MyDbContext()) // Or get it with DI, depends on your application
{
var count = context.SelectDynamic("samaster").Where(d => d["CODE"] == productBrandCode && d["WAREHOUSE"] == warehouse).Count();
}
Beware, however, that this is an expensive operation if you have a lot of rows in your table!
An alternative approach to only fetch the relevant results would be to replace
command.CommandText = $"SELECT * FROM [{table}]";
with
command.CommandText = $"SELECT CODE FROM [samaster] WHERE CODE={productBrandCode} AND WAREHOUSE={warehouse}";
and pass the parameters as function parameters.
public IList<IDictionary<string, dynamic>> SelectDynamic(string productBrandCode, string warehouse)
{...
Also make sure to escape all parameters if they are in any way submitted by user input to prevent SQL Injection Attacks!
There are two common approaches :
A.
int numberOfRows = await appDbContext.Database.ExecuteSqlInterpolatedAsync($"SELECT CODE FROM [samaster] WHERE CODE={productBrandCode} AND WAREHOUSE={warehouse} ").Count();
B.
int numberOfRows = await appDbContext.Database.ExecuteSqlInterpolatedAsync($"SELECT count(*) FROM [samaster] WHERE CODE={productBrandCode} AND WAREHOUSE={warehouse} ").First();
Since nobody gave me the correct answer. I end up using the following.
public async Task<bool> IsAValidProduct(string productBrandCode)
{
int count = 0;
await using DbCommand command = appDbContext.Database.GetDbConnection().CreateCommand();
command.CommandText =
"SELECT COUNT(CODE) FROM [samaster] WHERE CODE=#productBrandCode AND WAREHOUSE=#warehouse ";
command.CommandType = CommandType.Text;
command.Parameters.Add(new SqlParameter("#productBrandCode", SqlDbType.VarChar)
{Value = productBrandCode});
command.Parameters.Add(new SqlParameter("#warehouse", SqlDbType.VarChar)
{Value = warehouse});
await appDbContext.Database.OpenConnectionAsync();
count = (int) await command.ExecuteScalarAsync();
await appDbContext.Database.CloseConnectionAsync();
return count == 1;
}
int c= dbObj.Database.ExecuteSqlRaw(sql); //User this code
I am running code to fill a dataset from a SQL Server table, but for some reason it is not filling the datatable. I have run the stored procedure with the values when stepping through the code and it returns rows. I have checked via SQL Server Profiler what command runs and then executed the same command through SQL Server Management Studio and it returns rows, but for some reason in Visual Studio it returns no rows, yet it is not returning any exception.
The code is :
try
{
using (SqlConnection cnn = new SqlConnection(ConfigurationManager.ConnectionStrings["Intranet"].ConnectionString))
{
switch (command)
{
case "radiobutton":
string school = ParameterString[0];
using (SqlDataAdapter da = new SqlDataAdapter(GET_TEMPLATE_NAMES, cnn))
{
DataTable dt = new DataTable();
da.SelectCommand.CommandType = CommandType.StoredProcedure;
da.SelectCommand.Parameters.AddWithValue("#TemplateforSchool", school);
cnn.Open();
da.Fill(dt);
lstTemplates.DataSource = dt;
lstTemplates.DataBind();
}
break;
case "listbox": // If the listbox triggers the callback then update the grid
switch (radSchools.SelectedItem.Text)
{
case "Junior School":
StartYear = 1;
EndYear = 5;
break;
case "Middle School":
StartYear = 6;
EndYear = 8;
break;
case "Senior School":
StartYear = 9;
EndYear = 12;
break;
}
string TemplateName = ParameterString[0];
int DetentionCount = Convert.ToInt16(ParameterString[1]);
string DetentionType = ParameterString[2];
using (SqlDataAdapter da = new SqlDataAdapter(GET_CAREGIVER_EMAIL_LIST, cnn))
{
DataTable dt = new DataTable();
da.SelectCommand.CommandType = CommandType.StoredProcedure;
da.SelectCommand.Parameters.Add(new SqlParameter
{
ParameterName = "#DetentionType",
SqlDbType = SqlDbType.VarChar,
Value= DetentionType
});
da.SelectCommand.Parameters.Add(new SqlParameter
{
ParameterName="#DetentionCounter",
SqlDbType=SqlDbType.Int,
Value=DetentionCount
});
da.SelectCommand.Parameters.Add(new SqlParameter
{
ParameterName = "#StartYear",
SqlDbType = SqlDbType.Int,
Value = StartYear
});
da.SelectCommand.Parameters.Add(new SqlParameter
{
ParameterName = "#EndYear",
SqlDbType = SqlDbType.Int,
Value = EndYear
});
cnn.Open();
da.Fill(dt);
gdvCaregiverEmailList.DataSource = dt;
gdvCaregiverEmailList.DataBind();
}
break;
}
}
}
catch(Exception ex)
{
}
The problem is in the listbox: section of the switch statement. The SqlDataAdapter works fine in the radio button section of the switch.
I am not sure what I am missing so any help would be much appreciated.
I have checked SqlDataAdapter does not fill DataSet and also SqlDataAdapter not filling DataTable but the first was not relevant to my situation and the second had no accepted answer that I could find.
The SQL Server stored procedure that I am calling is
ALTER PROCEDURE [dbo].[GetCaregiverEmailList]
#DetentionType CHAR(2),
#DetentionCounter INT,
#StartYear INT,
#EndYear INT
AS
BEGIN
SELECT DISTINCT
sdc.StudentCode, s.Student#, s.GIVEN_NAME, s.SURNAME,
sdc.DetentionCount, sdc.DetentionType,
s.GIVEN_NAME + ' ' + s.SURNAME AS FullName,
LTRIM(s.CURRENT_YEAR) AS CurrentYear,
i.EMAIL_HOME, RTRIM(i.SALUTATION) AS EmailTitle,
ax.CORR_PREFERENCE, ar.CAREGIVER_NO
FROM
StudentDetentionCounter sdc
LEFT JOIN
[PCSchool].[dbo].[Student] s ON sdc.StudentCode = s.STUDENT#
INNER JOIN
[PCSchool].[dbo].[ALUMREL] ar ON sdc.StudentCode = ar.CHILD#
LEFT JOIN
[PCSchool].[dbo].[IDENTITY] i ON ar.PARENT# = i.[MEMBER#]
LEFT JOIN
[PCSchool].[dbo].[ALUMREL_EX] ax ON ar.parent# = ax.PARENT#
WHERE
(ar.PARENT# <> ar.FAMILY_HASH)
AND ar.EN_EMAIL IN ('I','A')
AND (ax.CORR_PREFERENCE = 1)
AND (sdc.DetentionCount = #DetentionCounter
AND sdc.DetentionType = #DetentionType)
AND (CONVERT(INT, (LTRIM(s.CURRENT_YEAR))) BETWEEN #StartYear AND #EndYear)
ORDER BY
s.SURNAME
END
The command that is executed when I look at SQL Server Profiler is
exec GetCaregiverEmailList #DetentionType='LT',#DetentionCounter=3,#StartYear=9,#EndYear=12
And the following are the rows that are returned if I execute the stored procedure manually or running the above exec command.
Any suggestions or help would be much appreciated.
Regards,
Darren
i have column like this (all in varchar)
SIGN ON SIGN OFF SIGN IN SIGN OFF
----------------------- -------- -----------
01-05-2015 / 20-04-2016 NULL NULL
AND i want to do this:
SIGN ON SIGN OFF SIGN IN SIGN OFF
----------------------- -------- -----------
01-05-2015 / 20-04-2016 01-05-2015 20-04-2016
i try with string split from sql 2016 but he had on another line the values not the same.
how can i do it?
You can try below -
DEMO
select
left(colname,charindex('/',colname)-1) as signon,
right(colname,charindex('/',colname)-1) as signoff
from tablename
OUTPUT:
signon signoff
01-05-2015 20-04-2016
You can use string functions:
select col1,
left(col1, charindex('/', col1) - 1),
stuff(col1, 1, charindex('/', col1) + 1, '')
from t;
You can try the following query.
select substring([SIGN ON SIGN OFF],1,charindex('/',[SIGN ON SIGN OFF])-2) as sign_in,
substring([SIGN ON SIGN OFF],charindex('/',[SIGN ON SIGN OFF])+1,len([SIGN ON SIGN OFF])) as sign_off
if i enter manually both answers worked. but for some reason executing was give me an error. so i give up from trying in the Sql code i did in c#.
here my solution that worked fo all cells.
DBHelper.DBHelper da = new DBHelper.DBHelper();
DataTable dt = new DataTable();
dt = da.getAllEmbarques();
foreach (DataRow row in dt.Rows)
{
try
{
List<SqlParameter> p = new List<SqlParameter>();
string nospaces = row[5].ToString().Replace(" ", string.Empty);
string[] newsplit = nospaces.Split('/');
if (newsplit.Count() == 1)
{
string[] newsplit1 = newsplit[0].ToString().Split('-');
DateTime dt1 = new DateTime(Int32.Parse(newsplit1[0]), Int32.Parse(newsplit1[1]), Int32.Parse(newsplit1[2]));
SqlParameter parameter = new SqlParameter("#SIGNIN",SqlDbType.DateTime);
parameter.IsNullable = true;
parameter.Value = dt1;
p.Add(parameter);
parameter = new SqlParameter("#SIGNOFF", SqlDbType.DateTime);
parameter.IsNullable = true;
parameter.Value = DBNull.Value;
p.Add(parameter);
p.Add(new SqlParameter("#id", row[7].ToString()));
da.UpdateStringsplitEmbarques(p.ToArray());
}
else
if (newsplit.Count() == 2)
{
string[] newsplit1 = newsplit[0].ToString().Split('-');
string[] newsplit2 = newsplit[1].ToString().Split('-');
DateTime dt1 = new DateTime(Int32.Parse(newsplit1[0]), Int32.Parse(newsplit1[1]), Int32.Parse(newsplit1[2]));
DateTime dt2 = new DateTime(Int32.Parse(newsplit2[0]), Int32.Parse(newsplit2[1]), Int32.Parse(newsplit2[2]));
SqlParameter parameter = new SqlParameter("#SIGNIN", SqlDbType.DateTime);
parameter.IsNullable = true;
parameter.Value = dt1;
p.Add(parameter);
parameter = new SqlParameter("#SIGNOFF", SqlDbType.DateTime);
parameter.IsNullable = true;
parameter.Value = dt2;
p.Add(parameter);
p.Add(new SqlParameter("#id", row[7].ToString()));
da.UpdateStringsplitEmbarques(p.ToArray());
}
}
catch
{
}
}
thanks you all for the time spending answering my question.
best regards
I want to pass a collection of IDs (via table-valued parameter) to an NHibernate IQuery select statement to be used in a join:
In native SQL, I can do this (SQLSelectData below). Notice there is no :param in the SqlCommand sql:
public static bool SQLSelectData()
{
string conACME = System.Configuration.ConfigurationManager
.AppSettings["conACME"].ToString();
DataTable tblBusUnit = new DataTable();
tblBusUnit.Columns.Add("VALUE", typeof(int));
DataRow dRow = tblBusUnit.NewRow();
dRow["Value"] = 1;
tblBusUnit.Rows.Add(dRow);
dRow = tblBusUnit.NewRow();
dRow["Value"] = 6;
tblBusUnit.Rows.Add(dRow);
using (SqlConnection con = new SqlConnection(conACME))
{
con.Open();
SqlDataReader rdr;
SqlCommand cmd = new SqlCommand(
"select bus_unit_id, BusUnit " +
"from BusUnit b " +
"join #tvpBusUnit s on s.value = b.BUS_UNIT_ID;",
con);
cmd.Parameters.Add(
new SqlParameter()
{
ParameterName = "#tvpBusUnit",
SqlDbType = SqlDbType.Structured,
TypeName = "dbo.[DLTableTypeInt]",
Value = tblBusUnit
});
rdr = cmd.ExecuteReader();
while (rdr.Read())
{
string stBusUnitId = rdr["bus_unit_id"].ToString();
string strBusUnit = rdr["BusUnit"].ToString();
Console.WriteLine("Bus Unit:" + strBusUnit);
}
}
return true;
}
How do I do this in NHibernate? I tried the accepted solution of this question by using the Sql2008Structured and Structured2008Extensions classes.
See code below that calls the SetStructured():
public void SQLSelectTVP<T>()
{
objNSession = NHibernateHelper.GetCurrentSession(strConn);
DataTable tblBusUnit = new DataTable();
tblBusUnit.Columns.Add("VALUE", typeof(int));
DataRow dRow = tblBusUnit.NewRow();
dRow["Value"] = 1;
tblBusUnit.Rows.Add(dRow);
dRow = tblBusUnit.NewRow();
dRow["Value"] = 6;
tblBusUnit.Rows.Add(dRow);
StringBuilder sbSQL = new StringBuilder();
sbSQL.Length = 0;
sbSQL.Append("select bus_unit_id, Business_Unit " +
"from tblBUSINESS_UNIT b " +
"join #tvpBusUnit s on s.value = b.BUS_UNIT_ID");
IQuery sqlQuery = objNSession.CreateSQLQuery(sbSQL.ToString());
sqlQuery.SetStructured("tvpBusUnit", tblBusUnit);
var lstQR = sqlQuery.List<T>();
}
However, it errors because there is no :param in the SQL:
Parameter tvpBusUnit does not exist as a named parameter in [select bus_unit_id, Business_Unit from tblBUSINESS_UNIT b join #tvpBusUnit s on s.value = b.BUS_UNIT_ID]
How can I fix this?
From the link you posted, I think the way you are accessing the structured variable is incorrect.
s.CreateSQLQuery("EXEC some_sp #id = :id, #par1 = :par1")
.SetStructured("id", dt)
Your code does not use the :id part, that is, :tvpBusUnit.
Also note that the TableType (in TypeName) may have to be created on the DB beforehand. Please check if this is required. From your code:
TypeName = "dbo.[DLTableTypeInt]",
Some discussion on passing table value parameters is provided here but NHibernate has an update without having to create types like this:
Passing table valued parameters to NHibernate. But this may need a pull-request. The answer provided in the other post you referred to allows you to create such types, one for each TableType.