Reputation: 61729
If you take a look at our arcade homepage:
In the top right there is a box showing the last people who played this game. In the profiler I'm using, it shows this as taking 900ms to run which is about 80% of the total page load time.
The query is relatively simple:
// Recent players
using (MainContext db = new MainContext())
{
var q = (from c in db.tblArcadeGamePlays
join a in db.tblProfiles on c.UserID equals a.UserID
where c.UserID != 0
select new
{
c.UserID,
c.tblForumAuthor.Username,
a.EmailAddress,
Date = (from d in db.tblArcadeGamePlays where d.UserID == c.UserID orderby d.Date descending select new { d.Date }).Take(1).Single().Date
})
.Distinct()
.OrderByDescending(c => c.Date)
.Take(16);
But it's too slow for my needs.
An output cache on this would not be suitable because it would be nice for this box to be in real time. Also, 900ms ontop of normal page load is too slow even for one user every now and then so would like to avoid that if possible.
Does anyone have any ideas on how I can speed this up? My two ideas at the moment are to have:
Both sort of ugly! Any help appreciated.
As requested, linqpad results
Lambda
TblArcadeGamePlays
.Join (
TblProfiles,
c => c.UserID,
a => a.UserID,
(c, a) =>
new
{
c = c,
a = a
}
)
.Where (temp0 => (temp0.c.UserID != 0))
.Select (
temp0 =>
new
{
UserID = temp0.c.UserID,
Username = temp0.c.User.Username,
EmailAddress = temp0.a.EmailAddress,
Date = TblArcadeGamePlays
.Where (d => (d.UserID == temp0.c.UserID))
.OrderByDescending (d => d.Date)
.Select (
d =>
new
{
Date = d.Date
}
)
.Take (1)
.Single ().Date
}
)
.Distinct ()
.OrderByDescending (c => c.Date)
.Take (16)
SQL
-- Region Parameters
DECLARE @p0 Int = 0
-- EndRegion
SELECT TOP (16) [t6].[UserID], [t6].[Username], [t6].[EmailAddress], [t6].[value] AS [Date2]
FROM (
SELECT DISTINCT [t5].[UserID], [t5].[Username], [t5].[EmailAddress], [t5].[value]
FROM (
SELECT [t0].[UserID], [t2].[Username], [t1].[EmailAddress], (
SELECT [t4].[Date]
FROM (
SELECT TOP (1) [t3].[Date]
FROM [tblArcadeGamePlays] AS [t3]
WHERE [t3].[UserID] = [t0].[UserID]
ORDER BY [t3].[Date] DESC
) AS [t4]
) AS [value]
FROM [tblArcadeGamePlays] AS [t0]
INNER JOIN [tblProfile] AS [t1] ON [t0].[UserID] = [t1].[UserID]
INNER JOIN [tblForumAuthor] AS [t2] ON [t2].[Author_ID] = [t0].[UserID]
) AS [t5]
WHERE [t5].[UserID] <> @p0
) AS [t6]
ORDER BY [t6].[value] DESC
Query Plan
Upvotes: 1
Views: 222
Reputation: 1496
Run the SQL Server Database Tuning Wizard ( in SQL Management Studio / Tools) against your database with these queries and let it create statistics and indexes to tune your database for performance. How many people would recommend this? It works.
Upvotes: 0
Reputation: 31586
This might not be the issue, but your date query could be simplified to this:
Date = (from d in db.tblArcadeGamePlays
where d.UserID == c.UserID
orderby d.Date descending
select d.Date).First()
Maybe the round-about query was confusing the optimizer. Otherwise I agree with the other answers, check the generated SQL query and check your indices.
Also, are you sure Distinct
is necessary and that it does what you think? It will only filter out duplicates that have the same value for every field/column.
Upvotes: 1
Reputation: 51319
I'd be willing to bet pretty good money that virtually all of the delay you are seeing comes from the database itself, not the LINQ (making this a database optimization question, not a LINQ optimization question).
I'd use linqpad to have a peek at what query is being generated (see: http://www.thereforesystems.com/view-t-sql-query-generated-by-linq-to-sql-using-linqpad/), and post that here. A query plan from running that query in SQL Management Studio (assuming that you are using SQL Server) would also be helpful.
OK, given the edits, try something like this. It should simplify the query dramatically:
using (MainContext db = new MainContext())
{
var latestIds = db.tblArcadeGamePlays.OrderByDescending(c => c.Date).Select(c => c.UserID).Distinct().Take(16); // These are the 16 most recent player Ids.
// join them here to the rest of those player's data
var playerData = ... // you'll need to fill in here some by filtering the other data you want using latestIds.Contains
}
Upvotes: 5