SQL Server CE - bad performance - c#

I'm developing an application that uses a SQL Server CE database and I am having trouble with the performance of the queries. For example, a simple query that gets the customer's last order:
SELECT
Customer.Name, Orders.Amount AS LastOrderAmount
FROM
Customers
LEFT JOIN
Orders ON Orders.OrderId IN (SELECT TOP(1) OrderId
FROM Orders
WHERE CustomerId = Customer.CustomerId
ORDER BY OrderNum DESC)
This query is incredibly slow. With only 30 costumers and about 300 orders it takes almost 10 seconds to finish!
I'm querying using SqlCEDataAdapter. I also tried using SqlCeResultSet, but the difference is negligible. Also, I have an index on Orders.CustomerId (creating it didn't make much difference, though).
Now, I'm not expecting miracles from SQL Server CE, but this is just terrible. So, is there something I can do to improve performance or is it just THAT slow?

Try this:
SELECT cus.Name
, outerOrd.Amount AS LastOrderAmount
FROM Customers cus
LEFT JOIN Orders outerOrd on outerOrd.CustomerId = cus.CustomerId
WHERE not exists (SELECT *
FROM Orders innerOrd
WHERE innerOrd.CustomerId = cus.CustomerId
and innerOrd.OrderId > outerOrd.OrderId
)
Edited, probably this is better:
SELECT cus.Name
, ord.Amount AS LastOrderAmount
FROM Customers cus
LEFT JOIN Orders ord on ord.CustomerId = cus.CustomerId
WHERE ord.OrderId = (SELECT max(OrderId)
FROM Orders
WHERE CustomerId = cus.CustomerId
)

Related

EF Core/SQL Join a different entity based on a condition

Lets suppose I have a table called Transactions
Transactions has the following columns
OrderId,
OrderType (Can be 0 = Sale or 1 = Purchase) <--- this can increase
Amount
Now I want to get the relevant data based on the OrderType
if OrderType = 0 then join from Sale Table else Join from Purchase Table.
Currently what I am doing is that doing three calls to the database to get the some other values from the other tables(which works but highly inefficient in long run as 3 Calls are bad performance wise).
My solution is using left join with SQL
SELECT ap.*,
coalesce(s.orderNo,p.orderNo) as orderNo
FROM apptransactions AS ap
LEFT JOIN sales AS s ON (ap.orderType = 0 and ap.orderId = s.id)
LEFT JOIN purchases AS p ON (ap.orderType = 1 and ap.orderId = p.id);
how can this query be converted to EF Core?
Why is your query not like this?
var results = context.Transactions.Select(t =>
new
{
/* t.column list, there's no t.* in LINQ */,
OrderNo = t.OrderType == 0 ? t.Sale.OrderNo : t.Purchase.OrderNo
});
Let EF generate any underlying joins it needs to, concern yourself with getting the results you want.
This also alludes to what #caius mentions. Your model is likely not high level enough or incorrectly mapped.

SQL query for paging received records

I want to write a query that receives informations about customers and their orders from another table and show them in this way:
Customer 1
Customer 1 Order 1
Customer 1 Order 2
Customer 2
Order 1 Customer 2
Customer 3
Order 1 Customer 3
....
And I want to do paging by Customers. For example - if I define items per page = 10, I want to show 10 customers, no matter how many orders they had.
I prepared this query but I think it's not gonna work properly and I don't know how to solve this..
var result = db.Page<Customer>(pageNumber, 10, "SELECT c.*, o.* FROM Customers c JOIN Orders o
ON o.Id = c.Id");
You can use dense_rank() and filter on that:
SELECT c.*, o.*, DENSE_RANK() OVER (ORDER BY c.id) as seqnum
FROM Customers c JOIN
Orders o
ON o.Customer_Id = c.Id;
You need to use WHERE for filtering, rather than LIMIT or FETCH or whatever.

Entity Framework v6.1 query compilation performance

I am confused how EF LINQ queries are compiled and executed. When I run a piece of program in LINQPad couple of times, I get varied performance results (each time the same query takes different amount of time). Please find below my test execution environment.
tools used: EF v6.1 & LINQPad v5.08.
Ref DB : ContosoUniversity DB downloaded from MSDN.
For queries, I am using Persons, Courses & Departments tables from the above DB; see below.
Now, I have below data:
Query goal: get the second person and associated departments.
Query:
var test = (
from p in Persons
join d in Departments on p.ID equals d.InstructorID
select new {
person = p,
dept = d
}
);
var result = (from pd in test
group pd by pd.person.ID into grp
orderby grp.Key
select new {
ID = grp.Key,
FirstName = grp.First().person.FirstName,
Deps = grp.Where(x => x.dept != null).Select(x => x.dept).Distinct().ToList()
}).Skip(1).Take(1).ToList();
foreach(var r in result)
{
Console.WriteLine("person is..." + r.FirstName);
Console.WriteLine(r.FirstName + "' deps are...");
foreach(var d in r.Deps){
Console.WriteLine(d.Name);
}
}
When I run this I get the result and LINQPad shows time taken value from 3.515 sec to 0.004 sec (depending how much gap I take between different runs).
If I take the generated SQL query and execute it, that query always runs between 0.015 sec to 0.001sec.
Generated query:
-- Region Parameters
DECLARE #p0 Int = 1
DECLARE #p1 Int = 1
-- EndRegion
SELECT [t7].[ID], [t7].[value] AS [FirstName]
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [t6].[ID]) AS [ROW_NUMBER], [t6].[ID], [t6].[value]
FROM (
SELECT [t2].[ID], (
SELECT [t5].[FirstName]
FROM (
SELECT TOP (1) [t3].[FirstName]
FROM [Person] AS [t3]
INNER JOIN [Department] AS [t4] ON ([t3].[ID]) = [t4]. [InstructorID]
WHERE [t2].[ID] = [t3].[ID]
) AS [t5]
) AS [value]
FROM (
SELECT [t0].[ID]
FROM [Person] AS [t0]
INNER JOIN [Department] AS [t1] ON ([t0].[ID]) = [t1].[InstructorID]
GROUP BY [t0].[ID]
) AS [t2]
) AS [t6]
) AS [t7]
WHERE [t7].[ROW_NUMBER] BETWEEN #p0 + 1 AND #p0 + #p1
ORDER BY [t7].[ROW_NUMBER]
GO
-- Region Parameters
DECLARE #x1 Int = 2
-- EndRegion
SELECT DISTINCT [t1].[DepartmentID], [t1].[Name], [t1].[Budget], [t1]. [StartDate], [t1].[InstructorID], [t1].[RowVersion]
FROM [Person] AS [t0]
INNER JOIN [Department] AS [t1] ON ([t0].[ID]) = [t1].[InstructorID]
WHERE #x1 = [t0].[ID]
My questions:
1) Are those LINQ statements correct? Or can they be optimized?
2) Is the time difference for LINQ query execution normal?
Another different question:
I have modified the first query to execute immediately (called ToList before the second query). This time generated SQL is very simple as shown below (it doesn't look like there is a SQL query for the first LINQ statement with ToList() included):
SELECT [t0].[ID], [t0].[LastName], [t0].[FirstName], [t0].[HireDate], [t0]. [EnrollmentDate], [t0].[Discriminator], [t1].[DepartmentID], [t1].[Name], [t1]. [Budget], [t1].[StartDate], [t1].[InstructorID], [t1].[RowVersion]
FROM [Person] AS [t0]
INNER JOIN [Department] AS [t1] ON ([t0].[ID]) = [t1].[InstructorID]
Running this modified query also took varied amount of time but the difference is not as big as the first query set run.
In my application, there going to be lot of rows and I prefer first query set to second one but I am confused.
Please guide.
(Note: I have a little SQL Server knowledge so, I am using LINQPad to fine tune queries based on the performance)
Thanks

How to translate SQL statement with multiple join conditions based on subquery to LINQ

This might be one of those situations where plain SQL commands are better than LINQ. Here's a simplified version of the SQL statement I'm trying to translate:
SELECT * FROM IDTable AS idt
INNER JOIN NameTable AS nt ON nt.IDTableID=idt.Id
AND nt.Id= (SELECT TOP(1) Id
FROM NameTable AS nt2
WHERE nt2.IDTableID=11 ORDER BY nt2.DateInserted DESC)
I have the LINQ query to pull records when just joining on IDs and I've seen how to join on multiple columns, but I have no idea how to plug the subquery into the mix.
If this isnt entirely clear, please let me know and I'll edit to elaborate.
Maybe something like this?
var results = from id in db.IDTable
join n in db.NameTable on id.Id equals n.IDTableID
where n.Id = (
from n2 in db.NameTable
where n2.IDTableID = 11
orderby n2.DateInserted desc
).First()
select new { id, n };

NHibernate Native SQL multiple joins

I"m having some problems with Nhibernate and native sql.
I've got an entity with alot of collections and I am doing an SQL Fulltext search on it. So when returning 100 or so entities, I dont want all collections be lazy loaded. For this I changed my SQL query:
SELECT Query.*
FROM (SELECT {spr.*},
{adr.*},
{adrt.*},
{cty.*},
{com.*},
{comt.*},
spft.[Rank] AS [Rak],
Row_number() OVER(ORDER BY spft.[Rank] DESC) AS rownum
FROM customer spr
INNER JOIN CONTAINSTABLE ( customerfulltext , computedfulltextindex , '" + parsedSearchTerm + #"' ) AS spft
ON spr.customerid = spft.[Key]
LEFT JOIN [Address] adr
ON adr.customerid = spr.customerid
INNER JOIN [AddressType] adrt
ON adrt.addresstypeid = adr.addresstypeid
INNER JOIN [City] cty
ON cty.cityid = adr.cityid
LEFT JOIN [Communication] com
ON com.customerid = spr.customerid
INNER JOIN [CommunicationType] comt
ON comt.communicationtypeid = com.communicationtypeid) as Query
ORDER BY Query.[Rank] DESC
This is how I setup the query:
var items = GetCurrentSession()
.CreateSQLQuery(query)
.AddEntity("spr", typeof(Customer))
.AddJoin("adr", "spr.addresses")
.AddJoin("adrt", "adr.Type")
.AddJoin("cty", "adr.City")
.AddJoin("com", "spr.communicationItems")
.AddJoin("comt", "com.Type")
.List<Customer>();
What happens now is, that the query returns customers twice (or more), I assume this is because of the joins since for each customer address, communicationItem (e.g. phone, email), a new sql row is returned. In this case I thought I could use the DistinctRootEntityResultTransformer.
var items = GetCurrentSession()
.CreateSQLQuery(query)
.AddEntity("spr", typeof(Customer))
.AddJoin("adr", "spr.addresses")
.AddJoin("adrt", "adr.Type")
.AddJoin("cty", "adr.City")
.AddJoin("com", "spr.communicationItems")
.AddJoin("comt", "com.Type")
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.List<Customer>();
Doing so an exception is thrown. This is because I try to list customers .List<Customer>() but the transformer returns only entities of the last join added. E.g. in the case above, the entity with alias "comt" is returned when doing .List() instead of .List<Customer>(). If I would switch last join with the join alias "cty", then the transformer returns a list of cities only...
Anyone knows how I can return a clean list of customers in this case?
try this
var items = GetCurrentSession()
.CreateSQLQuery(query)
.AddEntity("spr", typeof(Customer))
.AddJoin("adr", "spr.addresses")
.AddJoin("adrt", "adr.Type")
.AddJoin("cty", "adr.City")
.AddJoin("com", "spr.communicationItems")
.AddJoin("comt", "com.Type")
.AddEntity("spr", typeof(Customer))
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.List<Customer>();
GetCurrentSession.CreateQuery("select distinct spr from Customer spr inner join spr.Addresses
adr inner join adr.Type adrt inner join adr.City cty inner join apr.CommunicationItems com
inner join com.Type comt").List<Customer>();
Hope this helps. Or you can write the desired SQL query under
GetCurrentSession.CreateSqlQuery(sqlquery).List<Customer>();

Categories

Resources