Speed up sql JOIN

北慕城南 提交于 2019-12-06 14:17:06

First off, you can get rid of the second LEFT JOIN.

Your WHERE was removing out any matches, anyhow... For instance, if S.OrderID was 1 and there was a R.OrderID with a value of 1, the IS NULL enforcement in the WHERE wouldn't allow it. So it'll only return records where s.OrderID IS NULL, if I'm reading it correctly...

Secondly, if you're dealing with a large amount of data, adding on a NOLOCK table hint typically won't hurt. Assuming you don't mind the possibility of a dirty-read here or there :-P Usually worth the risk, though.

SELECT *
FROM [StaffEntry] s (nolock)
LEFT JOIN [MainFrame] m (nolock) ON m.ItemNumber = s.ItemNumber 
    AND m.Customer=s.Customer 
    AND m.CustomerPO = s.CustomerPO -- purchase order
    AND m.CustPORev = s.CustPORev  -- PO revision number
WHERE s.EntryDate BETWEEN @StartDate AND @EndDate
    AND s.OrderID IS NULL

Lastly, there was a part of your question which wasn't too clear for me...

"since I'm looking for records in the MainFrame table that don't exist, after doing the JOIN we have that ugly IS NULL in the where clause."

Ok... But are you trying to limit it to just where those MainFrame table records don't exist? If so, you'll want that expressed in the WHERE as well, right? So something like this...

SELECT *
FROM [StaffEntry] s (nolock)
LEFT JOIN [MainFrame] m (nolock) ON m.ItemNumber = s.ItemNumber 
    AND m.Customer=s.Customer 
    AND m.CustomerPO = s.CustomerPO -- purchase order
    AND m.CustPORev = s.CustPORev  -- PO revision number
WHERE s.EntryDate BETWEEN @StartDate AND @EndDate
    AND s.OrderID IS NULL AND m.ItemNumber IS NULL

If that's what you were intending with the original statement, perhaps you can get rid of the s.OrderID IS NULL check?

Before you even start looking at changing your query, you should ensure that all tables have a clustered index that makes sense for both this query and all other vital queries. Having clustered indexes on your tables i vital in sql server to ensure proper performance.

This doesn't make sense:

SELECT *
FROM [StaffEntry] s
LEFT JOIN [MainFrame] m ON m.ItemNumber = s.ItemNumber 
    AND m.Customer=s.Customer 
    AND m.CustomerPO = s.CustomerPO -- purchase order
    AND m.CustPORev = s.CustPORev  -- PO revision number
LEFT JOIN [Rejected] r ON r.OrderID = s.OrderID
WHERE s.EntryDate BETWEEN @StartDate AND @EndDate
    AND r.OrderID IS NULL AND s.OrderID IS NULL

if s.OrderID IS NULL, then r.OrderID = s.OrderID will never be true, so no rows from [Rejected] will ever be included, thus as given, it is equivalent to:

SELECT *
FROM [StaffEntry] s
LEFT JOIN [MainFrame] m ON m.ItemNumber = s.ItemNumber 
    AND m.Customer=s.Customer 
    AND m.CustomerPO = s.CustomerPO -- purchase order
    AND m.CustPORev = s.CustPORev  -- PO revision number
WHERE s.EntryDate BETWEEN @StartDate AND @EndDate
    AND s.OrderID IS NULL

Are you sure that code you posted is right?

In addition to what Kasperjj has suggested (which I do agree should be first), you might consider using temp tables to restrict the amount of data. Now, I know, I know that everyone says to stay away from temp tables. And i Usually do but sometimes, it is worth giving it a try because you can shrink the amount of data to join drastically with this method; this makes the overall query faster. (of course this does depend on how much you can shrink the result sets.)

My final thought is sometimes you will just need to experiment with different methods of pulling together the query. There might be too many variables for anyone here to give a answer.... On the other hand, people here are smart so I could be wrong.

Best of luck!

Regards, Frank

PS: I forgot to mention that if you wanted to try this temp table method, you'd also need to experiment with different indexes and primary keys on the temp tables. Depending on the amount of data, indexes and PKs can help.

Indexing on all the tables is going to be important. If you can't do much with the indexing on the [MainFrame] columns used in the join, you can also pre-limit the rows to be searched in [MainFrame] (and [Rejected], although that already looks like it has a PK)by specifying a date range - if the window of date should be roughly similar. This can cut down on the right hand side on that join.

I would also look at the execution plan and also do a simple black box evaluation of which of your JOINs is really the most expensive - m or r, by benchmarking the query with only one or the other. I would suspect it is m because of the multiple columns and missing useful indexes.

You could use m.EntryDate within a few days or months of your range. But if you already have indexes on Mainframe, the question is why aren't they being used, or if they are being used, why is the performance so slow.

try changing LEFT JOIN [Rejected] r with (nolock) ON r.OrderID = s.OrderID into the RIGHT MERGE JOIN:

SELECT ...
FROM [Rejected] r
     RIGHT MERGE JOIN [StaffEntry] s with (nolock) ON r.OrderID = s.OrderID
     LEFT JOIN [MainFrame] m with (nolock) ON....

Update:
In case it wasn't already obvious, I made a mistake in the code for the original question. That's now fixed, but unfortunately it means some of the better responses here are actually going the completely wrong direction.

I also have some statistics updates: I can make the query run nice and quick by severely limiting the data range used with StaffEntry.EntryDate. Unfortunately, I'm only able to do that because after running it the long way once I then know exactly which dates I care about. I don't normally know that in advance.

Tthe execution plan from the original run showed 78% cost for a clustered index scan on the StaffEntry table, and 11% cost on an index seek for the MainFrame table, and then 0% cost on the join itself. Running it using the narrow date range, that changes to 1% for an index seek of StaffEntry, 1% for an index seek of 'MainFrame', and 93% for a table scan of Rejected. These are 'actual' plans, not estimated.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!