how to optimize this part of code - sql-server

I want to optimize this code to minimum.
Dictionary<long, string> developmentRegions = objectset
.Where(f => f.IsDeleted == false && f.IsApproved == true)
.OrderBy(o => o.Name)
.ToDictionary(a => a.Id, a => a.Name);
Can anyone optimize this code

Your problem probaly is the order of query resul.
Try on SQL Server create a index on Name field, for take a fester result on order.
CREATE INDEX ixName ON TableWhereNameIsField (NameOfOrderField);

Assuming You really need all those names loaded from database, there is little You can do. You can remove the OrderBy, as Dictionary does not guarantee order anyway.

Related

Entity Framework Core Order By

I want to retrieve data from a database from a specific column sorted by the column, plus distinct. The data is also fetched distinctly, but not sorted. My query looks like this:
return context.TableName.OrderBy(l => l.ColumnName).Select(p => p.ColumnName)
.Distinct().ToList();
Distinct itself discards ordering. It is because of realisation and it depends on Database. So do ordering after Distinct.
return context.TableName
.Select(p => p.ColumnName)
.Distinct()
.OrderBy(l => l)
.ToList();

Will the database send over ALL the records if I don't use Take(1) before FirstOrDefault() in LINQ + Entity Framework?

Let's assume I'm using Entity Framework with a "Users" table, and 500,000 of these users have either blue eyes or brown eyes. Let's also assume that I'm connected to the database over the internet. I want to get the first User that satisfies this condition in LINQ, but I don't want the engine to send ALL the satisfying records over the internet.
public static User GetUser()
{
return DB.Users.Where(x => x.EyeColor == "blue" || x.EyeColor == "brown").Take(1).FirstOrDefault();
}
If I don't I apply Take(1) to the LINQ query, will the database send all 500,000 satisfying records over the internet? Or is the LINQ engine smart enough without Take(1) to tell the database only to send over one record?
Start with the documentation, then if in doubt with regards to what queries EF will actually use, run a Profiler against the database and you will see exactly what queries EF is running, which you can re-run against the database to review exactly what data is coming back.
With regards to First/FirstOrDefault yes, these will pull back one row without Take. What you do want to add though is an OrderBy clause to ensure the call is predictable. For instance, if there are 2 or more users with blue or brown eyes, which should be picked? Order By ID or a CreatedAt timestamp, or by age, etc.
You can specify filtering in a Where clause or as part of the FirstOrDefault
return DB.Users
.Where(x => x.EyeColor == "blue" || x.EyeColor == "brown")
.OrderBy(x => x.CreatedAt)
.FirstOrDefault();
is the same as:
return DB.Users
.OrderBy(x => x.CreatedAt)
.FirstOrDefault(x => x.EyeColor == "blue" || x.EyeColor == "brown");
Using Where is generally preferable as it lets you easily combine filter criteria conditionally.
Take is generally used with Skip to provide pagination where you want to retrieve sub-sets of data, like pages of 100 results at a time.
Note also that *AsDefault() methods will return #null if no data is found. Only use these if no matching data is a valid state. Anything using the results of a *AsDefault() method should be checking for Null.

Entity Framework Core 3 - can't get query to work

I am trying to construct a query using EF Core 3.1 across multiple tables, and it's simply not working.
I'll try to explain with some dummy examples.
Assume my SQL tables all have the following defined within the SQL Server DB:
Primary Keys (composite if needed)
Foreign Keys
The dummy Entities are as follows:
Town
Inhabitant
InhabitantCar
Car
Manufacturer
Mechanic
Foreign Key relationships:
Inhabitant -> Town
InhabitantCar -> Inhabitant and -> Car
Car -> Manufacturer and -> Mechanic
The Entities all have their Navigation Properties set, and I've set up the Primary and Foreign keys in the DB Context.
The vast majority of these tables have a bit field "Enabled", so rows can be disabled without deleting them
So the query I'm trying to write is similar to the following:
var data = await context.Town.AsNoTracking()
.Where(t => t.TownName == request.TownName)
.Include(t => t.Inhabitants.Where(i => i.Name == request.InhabitantName && i.Enabled)
.ThenInclude(i => i.InhabitantCar.Where(ic => ic.Enabled))
.ThenInclude(ic => ic.Cars.Where(c => c.Enabled))
.ThenInclude(c => c.Manufacturer.Where(m => m.Enabled))
.Include(t => t.Inhabitants.Where(i => i.Name == request.InhabitantName && i.Enabled)
.ThenInclude(i => i.InhabitantCar.Where(ic => ic.Enabled))
.ThenInclude(ic => ic.Cars.Where(c => c.Enabled))
.ThenInclude(c => c.Mechanic.Where(m => m.Enabled && m.Name == request.AllowedMechanic))
.ToListAsync().ConfigureAwait(false);
So to summarise, I want to know what cars are being driven by a "John Smith" who lives in "London" that are serviced by "MechanicsAreUs".
This seems quite long-winded to me, and that may be where my problem lies.
Anyhow, quite a few of the .WHERE clauses on the latter ThenIncludes just won't compile. Removing them one-by-one until it compiles gives me:
var data = await context.Town.AsNoTracking()
.Where(t => t.TownName == request.TownName)
.Include(t => t.Inhabitants.Where(i => i.Name == request.InhabitantName && i.Enabled)
.ThenInclude(i => i.InhabitantCar.Where(ic => ic.Enabled))
.ThenInclude(ic => ic.Cars)
.ThenInclude(c => c.Manufacturer)
.Include(t => t.Inhabitants.Where(i => i.Name == request.InhabitantName && i.Enabled)
.ThenInclude(i => i.InhabitantCar.Where(ic => ic.Enabled))
.ThenInclude(ic => ic.Cars)
.ThenInclude(c => c.Mechanic)
.ToListAsync().ConfigureAwait(false);
So, as written, it's going to bring back disabled entries, and I'm not specifying the mechanic. However, when I run it, I get the exception:
System.InvalidOperationException: Lambda expression used inside
Include is not valid.
I've spent ages going through the various Microsoft Examples, but I'm not finding any examples that seems to be of this level of complexity. It's only a handful of inner-joins. Something that could be accomplished in a stored procedure within minutes. Just that I want to do this using Entity Framework.
You cannot filter .Include(...) eager loads - it's all or nothing. As David Browne stated in a comment to your question, you should use query filters if you want to exclude records based on their Enabled flags, e.g.:
modelBuilder.Entity<Car>()
.HasQueryFilter(c => c.Enabled);
I seems you are interested in Car entities so let's restructure the query make that the focus:
var query = context.Cars;
You want cars associated with an Inhabitant with a specific name that is associated with a specific Town but is also serviced by a specific Mechanic, so let's filter by that criteria:
query = query.Where( c =>
c.InhabitantCar.Inhabitant.Name == request.InhabitantName
&& c.InhabitantCar.Inhabitant.Town.TownName == request.TownName
&& c.Mechanic == request.AllowedMechanic );
This query will now return the Car entities you desire, so let's now configure the eager loads:
query = query.Include( c => c.Manufacturer )
.Include( c => c.Mechanic )
.Include( c => c.InhabitantCar )
.ThenInclude( ic => ic.Inhabitant )
.ThenInclude( i => i.Town );
Give that a shot.
One recommendation was to use Query Filters.
The idea behind this was great - in my DB Context file I could add a common set of filters, e.g.
builder.Entity<Town>()
.HasQueryFilter(a => a.Enabled);
builder.Entity<Car>()
.HasQueryFilter(a => a.Enabled);
builder.Entity<Manufacturer>()
.HasQueryFilter(a => a.Enabled);
And that would be included in every query generated by my Service file - the Developers need not care.
However, when I analysed the resultant SQL, I found that my code was littered with multiple sub-queries, e.g.
Inner Join (Select...Where ...Enabled = 1)
On removing these centralized Query Filters and adding this to my WHERE clause in the LINQ statement resulted in a far more efficient query.
Entities reflect the data state. You cannot filter what related data you want, it's all or nothing. A town doesn't just have "enabled" inhabitants, it has inhabitants, just some of them are enabled and others aren't. That you don't want to view disabled ones or irrelevant ones, that is a view's concern, not the data model.
You can use Select to populate a model structure that is suited to your view. This can flatten out the joining tables, and load just the enabled records you want to see, plus streamline the fields your view needs rather than exposing everything about your domain. You can leverage AutoMapper to fill in the view models /w ProjectTo.

How to implement conditional ordering?

i am trying to found out how to create custom order function in cakephp3 order function in ORM find() method.
Let's suppose i have following model
User
name
...
custom_data
custom_data
type
every user has one custom data, where type is one of [ 20, 30, 40 ].
I need order by this type in following manner
if ( type == 20 ) {
// put in first positions
} else {
// put this records after users with custom_data->type != 20
}
I need to use it in paginator so i need somehing like
$this->Users
->find()
->where([ something ] )
->order( 'ASC' => here is my custom function )
Any suggestions?
First things first, from your description it sounds as if you need to use DESC, given that you say that you want records that match the condition first. In ascending order these records would come last.
That being said, the most simple way would be to use a basic condition, ie SQL wise something like:
ORDER BY type = 20 DESC
It could also be solved using a CASE statement, but it's not really neccessary if you have such simple requirements.
$query = $this->Users->find();
$query
->where(/* .. */)
->orderDesc(
$query->newExpr()->add(['type' => 20])
);
Further ordering statements can be added via additional calls to order(), orderAsc(), orderDesc().
See also
API > \Cake\Database\Query::order()
API > \Cake\Database\Query::orderAsc()
API > \Cake\Database\Query::orderDesc()
Cookbook > Database Access & ORM > Query Builder > Selecting Data

T-SQL IsNumeric() and Linq-to-SQL

I need to find the highest value from the database that satisfies a certain formatting convention. Specifically, I would like to find the highest value that looks like
EU999999 ('9' being any digit)
select max(col) will return something like 'EUZ...' for instance that I want to exclude.
The following query does the trick, but I can't produce this via Linq-to-SQL. There seems to be no translation for the isnumeric() function in SQL Server.
select max(col) from table where col like 'EU%'
and 1=isnumeric(replace(col, 'EU', ''))
Writing a database function, stored procedure, or anything else of that nature is far down the list of my preferred solutions, because this table is central to my app and I cannot easily replace the table object with something else.
What's the next-best solution?
Although ISNUMERIC is missing, you could always try the nearly equivalent NOT LIKE '%[^0-9]%, i.e., there is no non-digit in the string, or alternatively, the string is empty or consists only of digits:
from x in table
where SqlMethods.Like(x.col, 'EU[0-9]%') // starts with EU and at least one digit
&& !SqlMethods.Like(x.col, '__%[^0-9]%') // and no non-digits
select x;
Of course, if you know that the number of digits is fixed, this can be simplified to
from x in table
where SqlMethods.Like(x.col, 'EU[0-9][0-9][0-9][0-9][0-9][0-9]')
select x;
You could make use of the ISNUMERIC function by adding a method to a partial class for the DataContext. It would be similar to using a UDF.
In your DataContext's partial class add this:
partial class MyDataContext
{
[Function(Name = "ISNUMERIC", IsComposable = true)]
public int IsNumeric(string input)
{
throw new NotImplementedException(); // this won't get called
}
}
Then your code would use it in this manner:
var query = dc.TableName
.Select(p => new { p.Col, ReplacedText = p.Col.Replace("EU", "") })
.Where(p => SqlMethods.Like(p.Col, "EU%")
&& dc.IsNumeric(p.ReplacedText) == 1)
.OrderByDescending(p => p.ReplacedText)
.First()
.Col;
Console.WriteLine(query);
Or you could make use MAX:
var query = dc.TableName
.Select(p => new { p.Col, ReplacedText = p.Col.Replace("EU", "") })
.Where(p => SqlMethods.Like(p.Col, "EU%")
&& dc.IsNumeric(p.ReplacedText) == 1);
var result = query.Where(p => p.ReplacedText == query.Max(p => p.ReplacedText))
.First()
.Col;
Console.WriteLine("Max: {0}, Result: {1}", max, result);
Depending on your final goal it might be possible to stop at the max variable and prepend it with the "EU" text to avoid the 2nd query that gets the column name.
EDIT: as mentioned in the comments, the shortcoming of this approach is that ordering is done on text rather than numeric values and there's currently no translation for Int32.Parse on SQL.
As you said, there is no translation for IsNumeric from LINQ to SQL. There are a few options, you already wrote database function and stored procedure down. I like to add two more.
Option 1: You can do this by mixing LINQ to SQL with LINQ to Objects, but when you've got a big database, don't expect great performance:
var cols = (from c in db.Table where c.StartsWith("EU") select c).ToList();
var stripped = from c in cols select int.Parse(c.Replace("EU", ""));
var max = stripped.Max();
Option 2: change your database schema :-)
My suggestion is to fall back to in-line SQL and use the DataContext.ExecuteQuery() method. You would use the SQL query you posted in the beginning.
This is what I have done in similar situations. Not ideal, granted, due to the lack of the type-checking and possible syntax errors, but simply make sure it is included in any unit tests. Not every possible query is covered by the Linq syntax, hence the existence of ExecuteQuery in the first place.

Resources