SQL statement where clause by two dates - sql

I am trying to write a Sql statement which filter the records based on two date variables entered by users. Here is my Sql statement:
public List<DistributionPacking> filterByDeliveryDate()
{
List<DistributionPacking> dateList = new List<DistributionPacking>();
using (var connection = new SqlConnection(FoodBankDB.connectionString)) // get your connection string from the other class here
{
SqlCommand command = new SqlCommand("SELECT d.id, d.packingDate, d.deliveryDate, b.name FROM dbo.Distributions d " +
"INNER JOIN dbo.Beneficiaries b ON d.beneficiary = b.id WHERE d.deliveryDate BETWEEN #fromDate AND #toDate", connection);
command.Parameters.AddWithValue("#fromDate", startDate);
command.Parameters.AddWithValue("#toDate", endDate);
connection.Open();
using (var dr = command.ExecuteReader())
{
while (dr.Read())
{
string id = dr["id"].ToString();
DateTime packingDate = DateTime.Parse(dr["packingDate"].ToString());
DateTime deliveryDate = DateTime.Parse(dr["deliveryDate"].ToString());
string name = dr["name"].ToString();
dateList.Add(new DistributionPacking(id, packingDate, deliveryDate, name));
}
}
}
However, it told me that Conversion failed when converting date and/or time from character string although my data type for packingDate and deliveryDate is DateTime. I wonder why is it so.
Thanks in advance.

Try something like this ...
SELECT d.id, d.packingDate, d.deliveryDate, b.name
FROM dbo.Distributions d INNER JOIN dbo.Beneficiaries b
ON d.beneficiary = b.id
WHERE d.deliveryDate
BETWEEN CAST(#fromDate AS DATETIME) AND CAST(#toDate AS DATETIME)

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")+"'";

MS SQL Query in C# - poor performance

I calculate outstanding customers balance in C# Winforms. The code below works, but it's slow. Is there any way to improve its performance?
public DataTable GetOutStandingCustomers()
{
decimal Tot = 0;
DataTable table = new DataTable();
SqlConnection con = null;
try
{
table.Columns.Add("Code", typeof(Int32));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("City", typeof(string));
table.Columns.Add("Tot", typeof(decimal));
string constr = ConfigHelper.GetConnectionString();
string query = "SELECT Code, Name,City FROM Chart WHERE LEFT(CODE,3)='401' AND Code > 401001 ";
string query0 = " SELECT(SELECT ISNULL( SUM(SalSum.Grand),'0' ) FROM SalSum WHERE SalSum.Code = #Code ) +( SELECT ISNULL(SUM(Journals.Amount),'0' ) FROM Journals WHERE Journals.DrCode = #Code ) -( SELECT ISNULL(SUM(RSalSum.Grand),'0' ) FROM RSalSum WHERE RSalSum.Code = #Code ) -( SELECT ISNULL(SUM(Journals.Amount),'0' ) FROM Journals WHERE Journals.CrCode = #Code )+(SELECT ISNULL(SUM(Chart.Debit),'0' ) FROM Chart WHERE Chart.Code = #Code) - (SELECT ISNULL(SUM(Chart.Credit), '0') FROM Chart WHERE Chart.Code = #Code)";
Person per = new Person();
con = new SqlConnection(constr);
SqlCommand com = new SqlCommand(query, con);
SqlCommand com0 = new SqlCommand(query0, con);
con.Open();
SqlDataReader r = com.ExecuteReader();
if (r.HasRows)
{
while (r.Read())
{
per.Name = Convert.ToString(r["Name"]);
per.City = Convert.ToString(r["City"]);
per.Code = Convert.ToString(r["Code"]);
com0.Parameters.Clear();
com0.Parameters.Add("#Code", SqlDbType.Int).Value = per.Code;
Tot = Convert.ToDecimal(com0.ExecuteScalar());
if (Tot != 0)
{
table.Rows.Add(per.Code, per.Name, per.City, Tot);
}
}
}
r.Close();
con.Close();
return table;
}
catch (Exception)
{
throw new Exception();
}
}
The performance problem is due to you retrieve all data from the server and filter data in the client using the complex computed expression that sum from seven tables:
if (Tot != 0)
{
table.Rows.Add(per.Code, per.Name, per.City, Tot);
}
This represent overhead over network plus you manually add the result to the datatable row by row.
The provided solution do filter in the server based on the computed expression using the CROSS APPLY
and auto create the datatable directly from the DataReader.
The benefit of CROSS APPLY is all columns are feasible to the main sql query, so you can filter on ToT column, filtering is done in the server (not the client).
public void SelctChart()
{
string sql2 = #"
select c.Code, c.Name,c.City ,oo.T
from chart c
cross apply
( select c.code,
(
(select ISNULL( SUM(SalSum.Grand),0 ) FROM SalSum WHERE SalSum.Code = c.code )
+( select ISNULL(SUM(j.Amount),0 ) FROM [dbo].[Jornals] j WHERE j.DrCode = c.code)
-( SELECT ISNULL(SUM(RSalSum.Grand),'0' ) FROM RSalSum WHERE RSalSum.Code = c.Code )
-( SELECT ISNULL(SUM(j.Amount),0 ) FROM [dbo].[Jornals] j WHERE j.CrCode = c.code )
+(SELECT ISNULL(SUM( c0.Debit),0 ) FROM [dbo].Chart c0 WHERE c0.Code = c.code)
- (SELECT ISNULL(SUM(c1.Credit), 0) FROM [dbo].Chart c1 WHERE c1.Code = c.code)
)T
) oo
where
oo.T >0
and LEFT(c.CODE,3)='401' AND c.Code > 401001
";
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(sql2, connection);
//in case you pass #code as a a parameter
//command.Parameters.Add("#code", SqlDbType.Int);
//command.Parameters["#code"].Value = code;
try
{
connection.Open();
var reader = command.ExecuteReader();
while (!reader.IsClosed)
{
DataTable dt = new DataTable();
// Autoload datatable
dt.Load(reader);
Console.WriteLine(dt.Rows.Count);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
You can modify the method and pass code as a parameter
In this case it seems you're looping through and performing multiple queries using multiple Codes, you're also querying Chart twice. In this case you'd want to use a LEFT JOIN from Chart to your other tables.
ON Chart.Code = Salsum.Code
ON Chart.Code = Journal.Code
for example.
You will have to look at GROUP BY as well because you're aggregating some table columns by using SUM.
You may also need to make sure that Code is indexed on the tables you're querying. As long as Code is often queried like this and comparatively rarely Updated or Inserted to, then indexing the Code column on these tables is probably appropriate.
Left Joins : https://technet.microsoft.com/en-us/library/ms187518(v=sql.105).aspx
Indexing: https://technet.microsoft.com/en-us/library/jj835095(v=sql.110).aspx
Sorry I wrote a book on you here, but optimization often leads to a long answer (especially with SQL).
tldr;
Use a LEFT JOIN, grouping by Code
Index the Code columns

Stored procedure wrong output

I have this stored procedure that returns data on a selected week, as in the Company, Name, Expected Work, and Work done between the 19/10/2015 and the 25/10/2015 (per example).
I have just recently added the Expected Work line and for some odd reason the output differs from one week to another, when the value should be the same.
Company1 - Christopher - 35 - 35 | On one week can give the following on another :
Company1 - Christopher - 350 - 35
I have just realized the value isn't correct when there is a value to Work done, if there is no work recorded the Expected Work has the right value.
Here is the procedure :
ALTER procedure [dbo].[spGetWeeklyActivityByEmployee]
#startDate date
, #endDate date
as
set datefirst 1 -- Monday
select
Company.Name as [Company]
, Employee.FirstName + ' ' + Employee.LastName as [Name]
, sum(UserActivity.Cost) as [Recorder Time]
, sum(Employee.ExpectedTime) as [Expected Time] // I have added this line, not sure if it's correct
from
dbo.Employee
inner join
dbo.Company on Company.CompanyId = Employee.CompanyId
left join
dbo.UserActivity on UserActivity.Login = Employee.Login
and UserActivity.CalendarDate >= #startDate
and UserActivity.CalendarDate <= #endDate
where
(Employee.EntranceDate is null
or YEAR(Employee.EntranceDate) < YEAR(#startDate)
or (YEAR(Employee.EntranceDate) = YEAR(#startDate)
and DATEPART(WEEK, Employee.EntranceDate) <= DATEPART(WEEK, #startDate)))
and (Employee.ExitDate is null
or YEAR(Employee.ExitDate) > YEAR(#endDate)
or (YEAR(Employee.ExitDate) = YEAR(#endDate)
and DATEPART(WEEK, Employee.ExitDate) >= DATEPART(WEEK, #endDate)))
group by
Company.Name, Employee.FirstName + ' ' + Employee.LastName
return 0
Am I missing something? Is the way I retrieve Expected Time wrong?
EDIT :
Here is the part in the code where I save the information in an array :
// create and open a connection object
SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
conn.Open();
// 1. create a command object identifying
// the stored procedure
SqlCommand cmd = new SqlCommand("spGetWeeklyActivityByEmployee", conn);
// 2. set the command object so it knows
// to execute a stored procedure
cmd.CommandType = CommandType.StoredProcedure;
// 3. add parameter to command, which
// will be passed to the stored procedure
cmd.Parameters.Add(new SqlParameter("#startDate", wk1));
cmd.Parameters.Add(new SqlParameter("#endDate", wk2));
// execute the command
rdr = cmd.ExecuteReader();
string[] tab_company = new string[31]; // Don't mind the sizes
string[] tab_name = new string[31];
string[] tab_expectedtime = new string[31];
string[] tab_rectime = new string[31];
int counter;
counter = 0;
while (rdr.Read())
{
tab_company[counter] = rdr["Company"].ToString();
tab_name[counter] = rdr["Name"].ToString();
tab_expectedtime[counter] = rdr["Expected Time"].ToString();
tab_rectime[counter] = rdr["Recorder Time"].ToString();
counter++;
}
Perhaps the change in value comes from here?
Just take away the SUM() from Employee.ExpectedTime and add this to your GROUP BY

How to Join two tables showing all records Where Table A is Not In Table B

I have a email marketing web application. I want to show which email contacts in (Table B) are not showing up in EmailContacts_Campaign (Table A). In addition, I want to filter table A by the CampaignId field. When I run the below code I get 0 records, yet I know there are a couple of thousand records there. Can anyone tell me where I am messing up?
SELECT * FROM TableA
LEFT JOIN TableB
ON TableA.EmailContactId = TableB.EmailContactId
WHERE TableA.CampaignId = 1
AND TableB.EmailContactId IS NULL
ORDER BY TableB.EmailContactId DESC
I want to show all email contacts in the EmailContact Table that are not showing up in the EmailContactCampaign table. Here is the actual code:
public List<EmailContact> GetNotAssignedContactsForCampaign(int campaignId)
{
string sqlCommand = "SELECT * FROM EmailContactCampaign LEFT JOIN EmailContact";
sqlCommand += " ON EmailContactCampaign.EmailContact_EmailContactId = EmailContact.EmailContactId";
sqlCommand += " WHERE EmailContactCampaign.EmailContact_EmailContactId = " + campaignId.ToString() AND EmailContact.EmailContactId IS NULL ;
sqlCommand += " ORDER BY EmailContact.EmailContactId DESC";
var emailContacts = new List<EmailContact>();
string CS = db.Database.Connection.ConnectionString;
using (SqlConnection con = new SqlConnection(CS))
{
con.Open();
SqlCommand cmd = new SqlCommand(sqlCommand, con);
//Create sql datareader
using (SqlDataReader sqlDataReader = cmd.ExecuteReader())
{
while (sqlDataReader.Read())
{
var emailContact = new EmailContact();
emailContact.Assigned = ((bool)sqlDataReader["Assigned"]);
emailContact.Cell1 = _crypto.DecryptAndSanitize(sqlDataReader["Cell1"] as string);
emailContact.Cell2 = _crypto.DecryptAndSanitize(sqlDataReader["Cell2"] as string);
emailContact.City = _crypto.DecryptAndSanitize(sqlDataReader["City"] as string);
emailContact.Company = _crypto.DecryptAndSanitize(sqlDataReader["Company"] as string);
emailContact.EmailAddress = _crypto.DecryptAndSanitize(sqlDataReader["EmailAddress"] as string);
emailContact.EmailContactId = (int)sqlDataReader["EmailContactId"];
emailContact.FullName = _crypto.DecryptAndSanitize(sqlDataReader["FullName"] as string);
emailContact.Hold = (bool)sqlDataReader["Hold"];
emailContact.Phone1 = _crypto.DecryptAndSanitize(sqlDataReader["Phone1"] as string);
emailContact.Phone2 = _crypto.DecryptAndSanitize(sqlDataReader["Phone2"] as string);
emailContact.State = _crypto.DecryptAndSanitize(sqlDataReader["State"] as string);
emailContact.Status = (Status)sqlDataReader["Status"];
emailContact.Zip = _crypto.DecryptAndSanitize(sqlDataReader["Zip"] as string);
emailContacts.Add(emailContact);
}
}
return (emailContacts);
}
}
Have you tried this?
SELECT * FROM tableB WHERE EmailContactId NOT IN (SELECT EmailContactId FROM tableA)
i think you got 0 probably because of this AND TableB.EmailContactId IS NULL
Please try this one
SELECT * FROM TableA
LEFT JOIN TableB
ON TableA.EmailContactId = TableB.EmailContactId
WHERE TableA.CampaignId = 1
ORDER BY TableB.EmailContactId DESC
I'm sorry my question was not clear enough. Did some digging and found the answer on another post. Sorry but I accidentally closed it and can't find it again. Anyway, here is my implementation of it.
SELECT * FROM EmailContact
WHERE NOT EXISTS
(SELECT * FROM EmailContactCampaign WHERE EmailContactCampaign.EmailContact_EmailContactId = EmailContact.EmailContactId AND EmailContactCampaign.Campaign_CampaignId = 1)
If i understood your question correctly, you are looking for B's that are not in A. But your query will return A's that are not in B. Turn it aroung (tableB left join tableA where a... is NULL)
Your problem was that you had it the wrong way around: your query would return all contacts from EmailContactCampaign that were not in EmailContact.
The correct solution for your problem would look like this:
SELECT * FROM EmailContact
WHERE EmailContactId NOT IN (
SELECT EmailContact_EmailContactId FROM EmailContactCampaign
WHERE Campaign_CampaignId = ?
)
ORDER BY EmailContact.EmailContactId DESC

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())
{
// ...
}
}
}