How can i speed this query linq please? - c#

I want to make the LINQ query in this method faster:
public string GeneraCodiceListaEventi(DateTime data)
{
string codice = String.Empty;
string[] range1 = new string[] { "08:00", "08:30", "09:00", "09:30", "10:00", "10:30", "11:00", "11:30", "12:00", "12:30", "13:00", "13:30", "14:00", "14:30", "15:00", "15:30", "16:00", "16:30", "17:00", "17:30", "18:00", "18:30", "19:00", "19:30", "20:00" };
string[] range2 = new string[] { "08:29", "08:30", "09:29", "09:59", "10:29", "10:59", "11:29", "11:59", "12:29", "12:59", "13:29", "13:59", "14:29", "14:59", "15:29", "15:59", "16:29", "16:59", "17:29", "17:59", "18:29", "18:59", "19:29", "19:59", "20:29" };
using (DatabaseDataContext contestoDB = new DatabaseDataContext())
{
contestoDB.ObjectTrackingEnabled = false;
for(int i=0; i<25; i++)
{
var eventi = (from db in contestoDB.Eventi
where db.DataPrenotazione.Date == data.Date && (db.DataPrenotazione.TimeOfDay >= TimeSpan.Parse(range1[i]) && db.DataPrenotazione.TimeOfDay <= TimeSpan.Parse(range2[i]))
select new
{
ID = db.ID,
IDCliente = db.IDCliente,
Note = db.Note,
Ore = db.DataPrenotazione.ToShortTimeString()
});
if (eventi.Any())
{
codice += "<li><span class='ora'>" + range1[i] + "</span><input type='checkbox' id='item-" + GetNumItem(range1[i]) + "'/><label for='item-" + GetNumItem(range1[i]) + "'>Espandi</label><ul>";
foreach (var e in eventi)
{
codice += "<li class='app'> " + e.Ore + " - " + GetNominativoClienteDaID(e.IDCliente) + CheckNota(e.Note);
}
codice += "</ul></li>";
}
else
{
codice += "<li><span class='ora'>" + range1[i] + "</span>" + noapp + "</li>";
}
}
}
return codice;
}
In this function I'll build a string to send to html using ajax and show in the browser. But how can I make the query faster? Is there an alternative?

i can give you at least 2 suggestion to get this method execute faster:
1) Instead of using 'string codice = String.Empty;' use a StringBuilder instead, like this: 'StringBuilder longString = new StringBuilder();'
You might need to add System.Text to the using references on top of your code file. StringBuilder is much faster then using a common string because of many reason you can read up here:
String vs. StringBuilder
2) Instead of using 2 arrays of string and then parsing those string EACH TIME You loop into Timespan objects, you should instead create 2 arrays of TIMESPANS. At the moment, you are converting strings to timespan 2 times (2 array) * 25 times (your loop), so, those are 50 conversion you don't need to do.
This is how you could slightly optimize your code.
Then, to optimize the db access, you should do only 1 query with all the results, and then building the html splitting the results through code.
More queries = more time

Other than 3dd's point, the speed of this query has almost nothing to do with your C# code, and everything to do with the database.
If Eventi and DataPrenotazione are both large and not correctly indexed, then the query will run slowly. It is best explored in SQL, and analysed with whatever tools your database provides for understanding query performance.

Related

Speed up the nested for loops in c#

I wrote a program and it's working fine. But the thing is, It's taking much time to process.
I'm having more then millions data in a data table. So really its very hard to wait till the process complete. I hear something about LinQ method. But i'm not familiar in that. Can any one help me to speed up my code.
foreach (var ch in lsCh)
{
foreach (var dt in lsDt)
{
foreach (var cs in lsCs)
{
DataRow[] result = Dtsrc.Select("Channel = '" + ch.ToString() + "' AND Date = '" + dt.ToString() + "' AND CPSTime = '" + cs + "'");
if (result.Length > 0)
{
//Some calculation
}
Dtsrc.AcceptChanges();
}
}
}
Since the types of the DataColumns and the lists are not clear you might have to change them accordingly. But maybe this shows you a more efficient way anyway:
I would use HashSet<T> instead of List<T>. If you can't change the type in general you could create instances by passing the lists to the constructor:
var setCh = new HashSet<int>(lsCh);
var setDt = new HashSet<DateTime>(lsDt);
var setCs = new HashSet<DateTime>(lsCs);
Now this query should be much more efficient:
var rowsToProcess = Dtsrc.AsEnumerable()
.Where(r => setCh.Contains(r.Field<int>("Channel")) && setDt.Contains(r.Field<DateTime>("Date")) && setCs.Contains(r.Field<DateTime>("CPSTime")));
foreach (DataRow row in rowsToProcess)
{
//Some calculation
}

C# and SQL Server multiple values

I'm wondering if its possible to make this easier.
So, I have an arraylist of strings called names and an arraylist of strings called last_name.
When I'm constructing select statement, I want to combine every entry in names with everything in last_name and search in database like:
SELECT * FROM DB WHERE (names='NAMES[0]' AND last_name='LAST_NAME[0]')
or (names='NAMES[0]' AND last_name='LAST_NAME[1]')
or (names='NAMES[1]' AND last_name='LAST_NAME[0]')
or (names='NAMES[1]' AND last_name='LAST_NAME[1]')
This is an example, In my project I have 6 Lists, and I need every combination, and the easiest way was to make for in for in for in for...
Thanks a lot
This should work by using a query like this :
SELECT *
FROM DB
WHERE Name IN (#name1,#name2,#name3) AND LastName IN (#lastName1, #lastName2, #lastName3)
You can build this query in a for-loop, like this :
var names = new[] {"John", "Peter"};
var lastnames = new[] { "Doe", "Waylander" };
var nameParams = "";
var lastNameParams = "";
var cnt = 0;
foreach (var name in names)
{
var nameString = "#name" + cnt;
if (cnt!=0)
{
nameParams += ",";
}
nameParams += nameString;
cmd.Parameters.Add(nameString, name);
cnt++;
}
cnt = 0;
foreach (var lastName in lastnames)
{
var lastNameString = "#lastName" + cnt;
if (cnt != 0)
{
lastNameParams += ",";
}
lastNameParams += lastNameString;
cmd.Parameters.Add(lastNameString, lastName);
cnt++;
}
cmd.CommandText = #"SELECT *
FROM DB
WHERE NAME IN (" + nameParams + #")
AND LastName IN (" + lastNameParams + ")";
var result = cmd.ExecuteReader();
The only limitation is the number of parameters (IIRC it is about 1000). Another good alternative would be a stored procedure, like already mentioned in the comments.

C# LINQ Dynamic Select with all type of data

I am implementing search by using dynamic LINQ, where the query gets column name and search value in runtime. In this way, I need to parse the data according to the column type-
if (isNumeric)
{
int x = Int32.Parse(txtHistorySearch.Text);
truncatedData = ((IQueryable<object>)rawData).Where(columnName + "=#0", x).ToList();
}
else if (DateTime.TryParse(txtHistorySearch.Text, out temp))
{
var parsedDt = DateTime.Parse(txtHistorySearch.Text);
var nextDay = parsedDt.AddDays(1);
truncatedData = ((IQueryable<object>)rawData).Where(columnName + ">= #0 && " + columnName + " < #1", parsedDt, nextDay).ToList();
}
else
{
truncatedData = ((IQueryable<object>)rawData).Where(columnName + "=#0", searchValue).ToList();
}
Can this be done for all data types using single where clause?
You can do this using expression trees.
https://msdn.microsoft.com/en-us/library/bb397951.aspx
https://msdn.microsoft.com/en-us/library/bb882637.aspx

How can I secure SQL parameters with entity framework SqlQuery?

I have this method that should take an unknown amount of id's.
I got this method almost done but it isnt secure yet for obvious reasons, i know i could write my own method to strip the parameters but i would be more comfortable by using some build in method for this.
Here is the method
public static List<LocationModel> FetchCitiesByAreas(IEnumerable<string> areas)
{
using (var db = new BoligEnt())
{
var sqlQuery = new StringBuilder();
var first = true;
sqlQuery.Append("SELECT DISTINCT a.city AS City, a.zip AS Zip ");
sqlQuery.Append("FROM zip_city AS a ");
sqlQuery.Append("WHERE country = 1 ");
foreach (var d in areas)
{
if (first)
{
sqlQuery.Append("AND a.area_id = '" + d + "'");
first = false;
}
else
{
sqlQuery.Append("OR a.area_id = '" + d + "'");
}
}
return db.Database.SqlQuery<LocationModel>(sqlQuery.ToString()).ToList();
}
}
i know it have this function built in but as i stated earlier i dont know the exact amount of ids that will come in
db.Database.SqlQuery<LocationModel>("SELECT * FROM table WHERE id = #p0 ;", id).ToList();
Thanks
While I completely agree with paqogomez, in that you should just use LINQ to do the query, the .SqlQuery has the ability to take a parameter array. You could change your statement to look like this:
var sqlQuery = new StringBuilder();
sqlQuery.Append("SELECT DISTINCT a.city AS City, a.zip AS Zip ");
sqlQuery.Append("FROM zip_city AS a ");
sqlQuery.Append("WHERE country = 1 ");
for (int i = 0; i < areas.Count; i++)
{
if (i == 0)
{
sqlQuery.Append("AND (a.area_id = #p" + i.ToString());
}
else
{
sqlQuery.Append(" OR a.area_id = #p" + i.ToString());
}
}
sqlQuery.Append(")");
var results = db.Database.SqlQuery<LocationModel>(sqlQuery.ToString(), areas.ToArray()).ToList();
I added the missing parenthesis needed to your query to correctly filter out the OR results as well. I've also taken the assumption that areas is something like a List, or at least something you can easily get the count from.
Why dont you just use Linq?
var locations = (from zip in db.zip_city
where areas.Contains(zip.area_id) && zip.Country == 1
select new LocationModel{
City = zip.City,
Zip = zip.Zip
})
.Distinct()
.ToList();
If you still want to parameterize your query, you need to use EntityCommand
Also note that your query will fail because you havent put parenthesis around your OR statements.
I suggest structuring your sql like this:
string sqlQuery =
#"SELECT DISTINCT a.city AS City, a.zip AS Zip
FROM zip_city AS a
WHERE country = 1 AND (1=0 "
for (int i = 0; i < areas.Count; i++)
{
sqlQuery.Append("OR a.area_id = #d" + i.ToString() + " ");
}
sqlQuery.Append(")");

WebMatrix internal search engine

I've wrriten a code which searchs the database but i don't know why when i search for some specific keywords it'll show other links which are unrelated. here's the code and the result.
Page.Title = "Catalog Search";
var db = Database.Open("Shopping");
var searchWords = Request["searchTerm"].Split(' ');
IEnumerable<dynamic> result = Enumerable.Empty<string>();
var sqlSelect = "SELECT ProductId, ProductTitle FROM Products WHERE " +
"ProductTitle LIKE #0";
foreach(var word in searchWords)
{
result = result.Concat(db.Query(sqlSelect, "%" + word + "%").ToList());
}
so i searched for "Samsung LCD" and here's the result.
Samsung - 15" Series 9 Ultrabook Laptop
Samsung - Galaxy Tab 2 7.0
Samsung - 32" Class - LCD
Samsung - 32" Class - LCD
i've seen a php code which is exactly what i want but unfortunately i don't know how to convert it. here's the php code.
$searchTerms = explode(' ', $bucketsearch);
$searchTermBits = array();
foreach ($searchTerms as $term) {
$term = trim($term);
if (!empty($term)) {
$searchTermBits[] = "bucketname LIKE '%$term%'";
}
}
...
$result = mysql_query("SELECT * FROM buckets WHERE ".implode(' AND ', $searchTermBits).");
and the result of the php search code.
SELECT * FROM buckets WHERE bucketname LIKE '%apple%' AND bucketname LIKE '%and%' AND bucketname LIKE '%pear%'
I took the liberty of adding a few null checks and such, but the code for C# would pretty much look like this:
// make sure search terms are passed in, and remove blank entries
var searchTerms = Request["searchTerms"] == null ?
new string[] {} :
Request["searchTerms"].Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
// build the list of query items using parameterization
var searchTermBits = new List<string>();
for (var i=0; i<searchTerms.Length; i++) {
searchTermBits.Add("bucketname LIKE #" + i);
}
// create your sql command using a join over the array
var query = "SELECT * FROM buckets";
if (searchTerms.Length > 0) {
query += " WHERE " + string.Join(" AND ", searchTermBits);
}
// ask the database using a lambda to add the %
var db = Database.Open("StarterSite");
var results = db.Query(query, searchTerms.Select(x => "%" + x + "%").ToArray());
// enjoy!
Response.Write(results.Count());
Let me know if you run into any more trouble!

Categories

Resources