Select from multiple tables based upon search term but good in performance - performance

I have a query in which I pass the search term to filter the list of Companies, either by Email or company Title which is stored in another table (TranslationTexts) as Text column for multiple locales.
The query runs fine but it is very heavy and takes time. How can I make it more efficient?
See Table Diagram Image
The query:
gm.ListData = context.Companies.ToList()
.Where(a => a.AspNetUser.Email.NullableContains(searchTerm) ||
a.TitleTranslation.TranslationTexts
.Where(b => b.Text.NullableContains(searchTerm)).Any()
).Select(c => new ListCompany
{
CompanyID = c.CompanyID,
EmailID = c.AspNetUser.Email,
Title = c.TitleTranslation.TranslationTexts.FirstOrDefault(d => d.Locale == Locale).Text
}).ToList();

Related

How to improve performance of LINQ query to get many fields from a table

Suppose, I have a table called Accounts which has information like
bool isManaged,
double CurrentTotalBalance,
double CurrentManageableBalance,
AccountDetails AccountDetail table,
around 20 fields and 5 different tables
I want to write a query that can select only the field that I need not all of the tables as it reduces my performance.
Currently, I have something like this,
This is just a sample code, but not the working code.
var accounts = await _context.Accounts
.Include(x => x.Plan)
.Include(x => x.AccountDetails)
.Where(x => x.Plan.Id == 123)
.ToListAsync();
Then I am going, over the accounts list and getting the required results.
It works great for smaller plans but when I have larger plans it loads so many accounts.
So, is there a way to get data from it,
I was thinking something like this,
This is just a sample code, but not the working code.
var accounts = await _context.Accounts
.Include(x => x.Plan)
.Include(x => x.AccountDetails)
.Where(x => x.Plan.Id == 123)
.Select(x => new
{
PlanAssets = x.Sum(x => x.CurrentTotalBalance),
PlanParticipants = x.Accounts.Count,
OutsideAssets = 123,
ManagedParticipants = x.Where(x => x.IsManaged == 1).Count()
})
.ToListAsync();
But the problem with this is I am getting a list of each account. I want to sum up all the CurrentTotalBalance in the accounts table.
Is there a way to get the list of those fields which are required only from the accounts table and query them? I am having a performance issue. Any recommendation to improve the performance would be appreciated.

NotSupportedException for LINQ Queries

I am trying to get a list of a database table called oracleTimeCards whose employee id equals to the employeeID in employees list. Here is what I wrote:
LandornetSQLEntities db = new LandornetSQLEntities();
List<OracleEmployee> employees = db.OracleEmployees.Where(e => e.Office.Contains(officeName) && e.IsActive == true).Distinct().ToList();
var oracleTimeCards = db.OracleTimecards.Where(c => employees.Any(e => c.PersonID == e.PersonID)).ToList();
Anyone has any idea?
I'm going to assume you're using Entity Framework here. You can't embed calls to arbitrary LINQ extension methods inside your predicate, since EF might not know how to translate these to SQL.
Assuming you want to find all the timecards for the employees you found in your first query, you have two options. The simplest is to have a navigation property on your Employee class, named let's say TimeCards, that points to a collection of time card records for the given employee. Here's how that would work:
var oracleTimeCards = employees
.SelectMany(e => e.TimeCards)
.ToList();
If you don't want to do this for whatever reason, you can create an array of employee IDs by evaluating your first query, and use this to filter the second:
var empIDs = employees
.Select(e => e.PersonID)
.ToArray();
var oracleTimeCards = db.OracleTimecards
.Where(tc => empIDs.Contains(tc.PersonID))
.ToList();

Prioritizing fields when matching multiple fields with linq

I have a database with fields like firstname lastname street and searchfield. Anything that match the search field will be in my search subset here is the linq logic :
if (!String.IsNullOrEmpty(searchString))
{
folders = folders.Where(p => p.SearchField.ToLower().Contains(searchString.ToLower()));
}
I can order it by name or firstname or whatever.
Now I would like to present the results so it prioritize the name field in relation to my search term.
For example if i look for Schmid i want to show first the people with the LastName that match Schmid then the firstname then the street ...etc
Any idea ?
I hope I understood it correctly
var res =
folders
.Where(item => item.FirstName == name)
.Union(folders.Where(item => item.LastName == name))
/* Add more Union-Where statements */
;
I think the best approach is to get the matching objects first and then proceed in memory:
var lower = searchString.ToLower();
folders = folders
.Where(p => p.SearchField.ToLower().Contains(lower))
.ToArray();
folders = folders
.OrderBy(f => !f.LastName.Contains(lower))
.ThenBy(f => !f.FistName.Contains(lower))
.ThenBy(f => !f. ...
If you do all the OrderBy's on the IQueryable the query will probably blow up, while the initial filter is the most important thing to use the database engine for.
Note that you cannot always show the items that match lower in LastName and then those with a match in FistName etc., because there may be items that have a match in both. I don't think you want to duplicate items, do you?

Entity Framework 4 search on combined fields

How do I search on two combined fields. The search should happen on the SQL end if possible.
So say I have a customer table with first name and last name. I would like users to be able to search on both of those columns using a single search box.
My query currently looks like this:
var query = DbContext.Customers
.Where(c => c.FirstName.Contains(search) || c.LastName.Contains(search));
but it should be something like
var query = DbContext.Customers
.Where(c => c.FullName.Contains(search));
It is not possible unless you have FullName column also mapped. The way around this problem can be String.Concat which is allowed in Linq-to-entities:
var query = DbContext.Customers
.Where(p => String.Concat(p.FirstName, " ", p.LastName)
.Contains(search));
You could use a computed column in the database and map that
e.g.
alter table Customer add FullName AS FirstName + ' ' + LastName
(Not pretty I know)

Inefficient 'ANY' LINQ clause

I have a query that pulls back a user's "feed" which is essentially all of their activity. If the user is logged in the query will be filtered so that the feed not only includes all of the specified user's data, but also any of their friends.
The database structure includes an Actions table that holds the user that created the action and a UserFriends table which holds any pairing of friends using a FrienderId and FriendeeId column which map to UserIds.
I have set up my LINQ query and it works fine to pull back the data I want, however, I noticed that the query gets turned into X number of CASE clauses in profiler where X is the number of total Actions in the database. This will obviously be horrible when the database has a user base larger than just me and 3 test users.
Here's the SQL query I'm trying to achieve:
select * from [Action] a
where a.UserId = 'GUID'
OR a.UserId in
(SELECT FriendeeId from UserFriends uf where uf.FrienderId = 'GUID')
OR a.UserId in
(SELECT FrienderId from UserFriends uf where uf.FriendeeId = 'GUID')
This is what I currently have as my LINQ query.
feed = feed.Where(o => o.User.UserKey == user.UserKey
|| db.Users.Any(u => u.UserFriends.Any(ufr => ufr.Friender.UserKey ==
user.UserKey && ufr.isApproved)
|| db.Users.Any(u2 => u2.UserFriends.Any(ufr => ufr.Friendee.UserKey ==
user.UserKey && ufr.isApproved)
)));
This query creates this:
http://pastebin.com/UQhT90wh
That shows up X times in the profile trace, once for each Action in the table. What am I doing wrong? Is there any way to clean this up?
I would split the query into two queries,
Find all friends for the user, select their user keys and put them into a list.
Filter the feed according to the userkey and the keys of her.
Here is my shot at it without knowing your exact itnerfaces and ojbects, but it shows the concept:
var friends = db.UserFriends
.Where(x => x.isApproved && (
x.Friender.UserKey == user.userKey ||
x.Friendee == user.userKey
)
)
.Select(x => x.userKey)
.Distinct()
.ToList();
feed = feed.Where(x => x.userKey == user.userKey || friends.Contains(x.UserKey));
This should yield a query similar to this
SELECT ...
FROM feed
WHERE userKey == 'userkey1' OR userKey in ('userKey2', 'userKey3', ...)

Resources