1 | select sg.*, |
Monday, May 4, 2009
Fastest way to check for existence in a table
In order to improve performance on one of the pages in our Java code, I was making a SQL query which, along with the typical section grade information, also pulls in a field to tell whether that particular grade type is in use. Between my own brains and some quick Google searching, this was the best query I could come up with:
I assumed that in the absence of any ordering, "select top 1 1" would be converted by SQL Server into a sort of "exists" statement, and would therefore be the fastest query for the job. But just out of curiosity, I ran a similar LINQ query in LINQPad to see what SQL would be generated. Based on those results, I created the following query:Although it's not as simple a query, I was able to drop the execution time from about 27 milliseconds to about 9 milliseconds.
Thursday, April 16, 2009
Enumerate across a date range in Linq using "yield"
I'm making a generic data table displayer which should be able to display any set of data that is formatted properly. It can either deduce the table headers based on the data it receives, or it can use ones that I specify via a list of key/value pairs. For example, by setting the value of this property:
... I can make the row headers display all of the Strings on the right side of the given DataPairs in the IEnumerable's order, and the data for each row of the table will be aligned to match the keys on the left side of the DataPairs.
But that would be kind of wasteful, wouldn't it? It would mean creating an entire List of dates, when all I need is to print a bunch of consecutive dates--something I should be able to do mathematically on the fly.
... will create an enumerable object that does exactly what I need it to, without the need for any extra classes or anything. Here's how I use it:
This simple LINQ statement then gives me an IEnumerable that iteratively creates new DatePairs as the program traverses it. You have to admit, that's pretty smooth. That means that if I decide to paginate the table results, I can use the Take() method, and the system won't even produce DataPairs for dates that I don't iterate over. I can also move my DaysInRange method into a common utility class where it can be accessed any time I need to traverse a range of dates. It's a great example of how we can use LINQ in conjunction with the yield operator to create simple, efficient code.
1 | IEnumerable<DataPair<object, String>> RowKeysWithHeaders |
Now, let's say I'm using the data returned by the query I mentioned yesterday, which will only include dates for which there are TrackedTime entries, but I want to display all dates within a given date range, and simply leave cells empty if there are no entries for those dates. Furthermore, I want to specify how the dates are formatted. I could create a list of date/String pairs to use as row keys with headers, like this:
1 | List<DataPair<object, String>> dateHeaders = new List<DataPair<object, String>>(); |
A more efficient way would be to create an IEnumerable class (and an accompanying IEnumerator class) that know how to iterate across dates. But that's a lot more work and a lot more code for something that should be relatively simple.
Thanks to the yield operator, there is a better way. This simple method:
1 | public static IEnumerable<DateTime> DaysInRange(DateTime startDate, DateTime endDate) |
1 | this.ReportTableDisplay1.RowKeysWithHeaders = from d in DaysInRange(startDate, endDate) |
It can also be used to highlight one of the dangers of accepting IEnumerable arguments. Since I literally have no idea how expensive it might be to iterate over a given IEnumerable, I need to make sure my ReportTableDisplay class only iterates over the RowKeysWithHeaders once. Otherwise I could end up creating who-knows-how-many copies of exactly the same DataPair without even realizing it. Just think how that would turn out if I was using a truly expensive IEnumerable--one whose IEnumerator begins by accessing data from over the Internet, for example!
Wednesday, April 15, 2009
Improved Dynamic Query
Yesterday I figured out how we could use C# Expressions to filter query results by any number of criteria. Today I refined my approach somewhat. Instead of the long, complex LINQ query I used there, I now have:
This produces a faster, more reasonable set of SQL code which returns almost identical data:
Note that I avoided the problem I mentioned here by returning a simple set of native types rather than Entity Framework objects. This approach wouldn't be best under normal circumstances. If I'm querying the database for usernames, for example, I will often want to have more user data as well (like their real names) available to my front-end code. In that case, I could benefit from using the Entity Framework to get all the object data in a single roundtrip. However, if I'm pulling data for reporting purposes, I generally know exactly what data I want to be displaying. In that case it's more important to keep the data set small and fast. I've seen poorly-designed reporting engines pull so much data from the database that the VM runs out of memory, which causes all sorts of problems.
Finally, rather than relying on the LINQ framework to package my objects into custom classes for me, I created a generic TableData class, along with an extender that allows me to do this:
The next step would be to create a generic web control that creates a front-end table when given the returned report data. That way we can use a single control to display all kinds of report data, rather than creating a new method to display each new set of report data.
1 | var userTimes = (from t in times |
1 | -- Region Parameters |
Finally, rather than relying on the LINQ framework to package my objects into custom classes for me, I created a generic TableData class, along with an extender that allows me to do this:
1 | public TableData<String, DateTime, int> generateData(List<Filter<TrackedTime>> dateFilters) |
Tuesday, April 14, 2009
Dynamic Queries in Entity Framework
I've been working on a proof-of-concept for building dynamic Linq queries in the Entity Framework. The idea is to have any number of criteria that a user can set to limit or expand the scope of (for example) a report.
I still have a long way to go before it would be ready to use for real, but here's what I've been able to do:
So I can pass the generateData method filters like this one:
and the Linq statement will auto-generate this crazy SQL code:
which, amazingly enough, rather efficiently produces exactly the information I wanted, and no more. Pretty slick, eh?
I still have a long way to go before it would be ready to use for real, but here's what I've been able to do:
1 | public class ReportPerUserPerDay2 |
1 | public partial class Controls_ReportFilters_FilterDateRange : System.Web.UI.UserControl, TimesheetReporting.Filters.Filter<TrackedTime> |
1 | -- Region Parameters |
Thursday, February 19, 2009
LinqPad, my new favorite tool
As I was looking into LINQ, I found a tool called LINQPad, which allows you to run LINQ queries on a typical SQL database. "Cool," I thought, "That might help me become familiar with LINQ syntax." I had no idea of the power of this tool.
When it first connects to your chosen database, LINQPad automatically generates a data-object mapping based on the foreign and primary keys in your database. So rather than having to worry about doing an INNER JOIN ON table1.some_column = table2.some_column, you can just use standard LINQ syntax to join the objects based on their relationships. Another advantage is that it provides links from one table to another, so you can easily jump around the table structure, seeing how things are interconnected.
Visualization of the returned data is much more intuitive and powerful than the simple grid that is returned by normal applications. For example, using the "select new" statement I discussed in my last posting, you can see lists of objects grouped within other objects, fully collapsible and everything! For this reason, I prefer it to SQL Server Management Studio for viewing information in the database.
LINQPad also lets you see what code is generated based on the LINQ queries you put in. You can see the Lambda code, the SQL code, and even the Instruction Language code that comes from the statements you write. That's very handy for understanding the performance of your code.
LINQPad's uses aren't limited to Linq to SQL, however! You can reference the .dll file from your Entity Framework data layer and run queries as if you were writing code right within Visual Studio. It holds true to its claim to be a "code snippet IDE." In fact, you don't even have to use it for LINQ! Say you just want to run a method and see what kind of data comes out: rather than creating a temporary unit-test in your Visual Studio directory, you can simply write the C# code snippet in LINQPad and run it!
If you're new to LINQ, LINQPad actually comes with a hearty set of code samples to help you get started.
Oh, yeah, and you can also use it to run SQL queries and commands if you want.
Overall, I've been extremely impressed by all that I can do with LINQPad. Now I'm trying to get my bosses to invest in an Autocomplete license for it, and looking forward to the increased productivity I can expect from that feature.
When it first connects to your chosen database, LINQPad automatically generates a data-object mapping based on the foreign and primary keys in your database. So rather than having to worry about doing an INNER JOIN ON table1.some_column = table2.some_column, you can just use standard LINQ syntax to join the objects based on their relationships. Another advantage is that it provides links from one table to another, so you can easily jump around the table structure, seeing how things are interconnected.
Visualization of the returned data is much more intuitive and powerful than the simple grid that is returned by normal applications. For example, using the "select new" statement I discussed in my last posting, you can see lists of objects grouped within other objects, fully collapsible and everything! For this reason, I prefer it to SQL Server Management Studio for viewing information in the database.
LINQPad also lets you see what code is generated based on the LINQ queries you put in. You can see the Lambda code, the SQL code, and even the Instruction Language code that comes from the statements you write. That's very handy for understanding the performance of your code.
LINQPad's uses aren't limited to Linq to SQL, however! You can reference the .dll file from your Entity Framework data layer and run queries as if you were writing code right within Visual Studio. It holds true to its claim to be a "code snippet IDE." In fact, you don't even have to use it for LINQ! Say you just want to run a method and see what kind of data comes out: rather than creating a temporary unit-test in your Visual Studio directory, you can simply write the C# code snippet in LINQPad and run it!
If you're new to LINQ, LINQPad actually comes with a hearty set of code samples to help you get started.
Oh, yeah, and you can also use it to run SQL queries and commands if you want.
Overall, I've been extremely impressed by all that I can do with LINQPad. Now I'm trying to get my bosses to invest in an Autocomplete license for it, and looking forward to the increased productivity I can expect from that feature.
Joining entities using the "new" operator
I took some time in my last post to explain that two database queries are better than many database queries. Namely, it's better to do one database query and create a mapping with the returned data, than it is to perform a new database query for every chunk of information that we want. (I should note that this is obviously only true if the results of your two database queries can be limited to a size that roughly fits the amount of data you'd otherwise be requesting. If you're clever, this can usually be the case.)
As I played around a bit more with Linq, I found another syntax that can be used to join objects together. For example, the following Linq (to SQL) code:
... will produce objects of a new Anonymous type with the following elements:
As I played around a bit more with Linq, I found another syntax that can be used to join objects together. For example, the following Linq (to SQL) code:
1 | from ec in EntityCategory |
- EntityCategory ec
- IEnumerable
te
- Because it creates an Anonymous data type, the returned results don't really lend themselves to being passed around your program as arguments.
- There is actually quite a bit of overhead associated with this method. The generated SQL query is a lot more expensive for the database, and the returned data set has a lot of unnecessarily-duplicated data. Once the data returns from the database, it has to be parsed and organized by the framework (Entity or Linq to SQL) in much the same way as the JoinedEntityMap class that I mentioned in my last post. So this syntax can run much more slowly, despite reducing the number of database round-trips.
Tuesday, February 17, 2009
Joining entities to avoid database roundtrips
In my last post, I hinted that I had an alternative in mind for the Eager Loading problem that has vexed me over the past few days.
First of all, it's important to understand what we intend to gain by eager loading--fewer database round-trips. You see, every time your program asks the database for information, there is considerable overhead with establishing the connection, waiting for your request to go over the wire, and waiting for the response to come back. If your queries return large data sets, this overhead will have a minimal impact. But if you're doing a whole bunch of tiny queries, this overhead can make a huge difference. In my case, where I often work from home and connect to the database at work, the overhead from these so-called "round-trips" can actually take more time than everything else in a typical page load.
So rather than saying:
... , I could save a lot of time by saying:
The folks who designed the Entity Framework were aware of this, and they took action to prevent round-trips as much as possible. For example:
With negligible memory overhead, the above code causes a grand total of two SQL queries and takes 110 milliseconds to run with 4 EntityCategories and 47 TrackedEntities, as opposed to the code below, which takes 251 milliseconds:
With that kind of difference on such a small scale, you can imagine how much of a difference it could make if you had hundreds, or even thousands of records in each table!
Another potential benefit to this approach is that the main page's code can create the data it knows will be needed, and then hand strongly-typed data to each user control in turn. It not only makes it possible for the user controls to be more specific in what sort of data they need as parameters, but it also increases the transparency of your data accesses. With most of your calls to the data layer occurring in one code file, it becomes obvious what's taking so much time to load. And if you're not using an HttpContext-based Entity context (which we are), it can also simplify the problems you'll run into with conflicting contexts.
First of all, it's important to understand what we intend to gain by eager loading--fewer database round-trips. You see, every time your program asks the database for information, there is considerable overhead with establishing the connection, waiting for your request to go over the wire, and waiting for the response to come back. If your queries return large data sets, this overhead will have a minimal impact. But if you're doing a whole bunch of tiny queries, this overhead can make a huge difference. In my case, where I often work from home and connect to the database at work, the overhead from these so-called "round-trips" can actually take more time than everything else in a typical page load.
So rather than saying:
1 | TrackedEntityBO tebo = new TrackedEntityBO(context); |
1 | TrackedEntityBO tebo = new TrackedEntityBO(context); |
- Each context instance can cache a lot of the data that it gathers. If a later LINQ query on the same context can be determined to contain only data that has previously been cached, the Framework will use the cached data rather than executing another query.
- Unlike Linq to SQL, the Entity Framework will not make a database query without being asked. For example, the following code will produce an exception because the EntityCategory object never got loaded into the context:
1
2
3TrackedEntity e = (from te in context.TrackedEntity
select te).First();
Console.WriteLine (e.EntityCategory.Name);
1 | IQueryable<TrackedEntity> teQuery = from te in context.TrackedEntity |
1 | IQueryable<EntityCategory> ecQuery = from ec in context.EntityCategory |
Another potential benefit to this approach is that the main page's code can create the data it knows will be needed, and then hand strongly-typed data to each user control in turn. It not only makes it possible for the user controls to be more specific in what sort of data they need as parameters, but it also increases the transparency of your data accesses. With most of your calls to the data layer occurring in one code file, it becomes obvious what's taking so much time to load. And if you're not using an HttpContext-based Entity context (which we are), it can also simplify the problems you'll run into with conflicting contexts.
Subscribe to:
Posts (Atom)