I have a query, or batch of queries, that I just can’t get to play nice 🙂
SELECT
C.ID AS cust_id,
C.State AS cust_state,
C.PrevState AS cust_previous_state,
C.ProjectID AS cust_imported_on_project_id,
C.CampaignID AS cust_imported_on_campaign_id,
C.Priority AS cust_priority,
C.Name AS cust_firstname,
C.Name2 AS cust_lastname,
C.AllocatedUser AS cust_allocated_user,
C.ED1 AS cust_social_security_number,
C.ED2 AS cust_customer_number,
C.ED3 AS cust_type,
C.ED4 AS cust_initial_fact_1,
C.ED5 AS cust_initial_fact_2,
C.ED6 AS cust_initial_fact_3,
C.ED7 AS cust_initial_fact_4,
C.ED8 AS cust_initial_fact_5,
C.ED9 AS cust_extra_1,
C.ED10 AS cust_extra_2,
CED2.ED11 AS cust_extra_3,
CED2.ED12 AS cust_extra_4,
CED2.ED13 AS cust_extra_5,
A.Serial AS address_serial,
A.PostAddress AS address_postal_address,
A.PostCode AS address_postal_code,
A.PostOffice AS address_city,
A.PhoneNr AS address_phonenumber,
A.FaxNr AS address_faxnumber,
A.EMail AS address_email,
A.Notes AS address_notes,
A.ED1 AS address_secondary_phonenumber,
A.ED2 AS address_origin_file,
A.State AS address_state
FROM TCustomers C WITH (NOLOCK)
LEFT JOIN TCustED2 CED2 WITH (NOLOCK) ON C.ID = CED2.CustomerID
LEFT JOIN TAddresses A WITH (NOLOCK) ON C.AddressNr = A.Serial AND C.ID = A.CustomerID
WHERE C.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND C.InsDate > '2011-04-03 00:00:00'
This query in itself is very snappy and executes on a million+ rows in a few seconds.
The problem is when I want to use information from the order table to filter my result.
I have tried to join on the Orders table
SELECT
C.ID AS cust_id,
C.State AS cust_state,
C.PrevState AS cust_previous_state,
C.ProjectID AS cust_imported_on_project_id,
C.CampaignID AS cust_imported_on_campaign_id,
C.Priority AS cust_priority,
C.Name AS cust_firstname,
C.Name2 AS cust_lastname,
C.AllocatedUser AS cust_allocated_user,
C.ED1 AS cust_social_security_number,
C.ED2 AS cust_customer_number,
C.ED3 AS cust_type,
C.ED4 AS cust_initial_fact_1,
C.ED5 AS cust_initial_fact_2,
C.ED6 AS cust_initial_fact_3,
C.ED7 AS cust_initial_fact_4,
C.ED8 AS cust_initial_fact_5,
C.ED9 AS cust_extra_1,
C.ED10 AS cust_extra_2,
CED2.ED11 AS cust_extra_3,
CED2.ED12 AS cust_extra_4,
CED2.ED13 AS cust_extra_5,
A.Serial AS address_serial,
A.PostAddress AS address_postal_address,
A.PostCode AS address_postal_code,
A.PostOffice AS address_city,
A.PhoneNr AS address_phonenumber,
A.FaxNr AS address_faxnumber,
A.EMail AS address_email,
A.Notes AS address_notes,
A.ED1 AS address_secondary_phonenumber,
A.ED2 AS address_origin_file,
A.State AS address_state
FROM TCustomers C WITH (NOLOCK)
LEFT JOIN TCustED2 CED2 WITH (NOLOCK) ON C.ID = CED2.CustomerID
LEFT JOIN TAddresses A WITH (NOLOCK) ON C.AddressNr = A.Serial AND C.ID = A.CustomerID
LEFT JOIN TOrders O WITH (NOLOCK) ON C.ID = O.CustomerID
WHERE C.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND
(C.InsDate > '2011-04-03 00:00:00' OR O.CustomerID NOT NULL OR O.[Date] > '2011-04-03 00:00:00' OR O.Exported IS NULL);
This results in an execution time of several minutes.
If I just run the interesting part from TOrders it just takes a few seconds to execute.
SELECT CustomerID
FROM TOrders
WHERE O.CustomerID NOT NULL
OR O.[Date] > '2011-04-03 00:00:00'
OR O.Exported IS NULL;
So the problem is when combining the two. I tried running the TOrders query and pasting the resulting CustomerID’s into the main query directly and that was snappy and took around 5-10 seconds. I tried prefetching the interesting data from TOrders and putting it into a temporary table but that didn’t make it any faster.
CREATE TABLE #ORDERCUSTOMERS (
CustomerID int
);
CREATE UNIQUE CLUSTERED INDEX IX_1 on #ORDERCUSTOMERS (CustomerID);
INSERT #ORDERCUSTOMERS SELECT DISTINCT O.CustomerID FROM LPD1_8.dbo.TOrders O WHERE O.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND (O.[Date] > '2011-04-03 00:00:00' OR O.Exported IS NULL);
SELECT
C.ID AS cust_id,
C.State AS cust_state,
C.PrevState AS cust_previous_state,
C.ProjectID AS cust_imported_on_project_id,
C.CampaignID AS cust_imported_on_campaign_id,
C.Priority AS cust_priority,
C.Name AS cust_firstname,
C.Name2 AS cust_lastname,
C.AllocatedUser AS cust_allocated_user,
C.ED1 AS cust_social_security_number,
C.ED2 AS cust_customer_number,
C.ED3 AS cust_type,
C.ED4 AS cust_initial_fact_1,
C.ED5 AS cust_initial_fact_2,
C.ED6 AS cust_initial_fact_3,
C.ED7 AS cust_initial_fact_4,
C.ED8 AS cust_initial_fact_5,
C.ED9 AS cust_extra_1,
C.ED10 AS cust_extra_2,
CED2.ED11 AS cust_extra_3,
CED2.ED12 AS cust_extra_4,
CED2.ED13 AS cust_extra_5,
A.Serial AS address_serial,
A.PostAddress AS address_postal_address,
A.PostCode AS address_postal_code,
A.PostOffice AS address_city,
A.PhoneNr AS address_phonenumber,
A.FaxNr AS address_faxnumber,
A.EMail AS address_email,
A.Notes AS address_notes,
A.ED1 AS address_secondary_phonenumber,
A.ED2 AS address_origin_file,
A.State AS address_state
FROM LPD1_8.dbo.TCustomers C WITH (NOLOCK)
LEFT JOIN LPD1_8.dbo.TCustED2 CED2 WITH (NOLOCK) ON C.ID = CED2.CustomerID
LEFT JOIN LPD1_8.dbo.TAddresses A WITH (NOLOCK) ON C.AddressNr = A.Serial AND C.ID = A.CustomerID
LEFT JOIN #ORDERCUSTOMERS O ON C.ID = O.CustomerID
WHERE C.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND
(C.InsDate > '2011-04-03 00:00:00' OR O.CustomerID NOT NULL);
DROP TABLE #ORDERCUSTOMERS;
So do you guys have any idea? This will reside in a stored procedure so I want to avoid dynamic SQL if possible. Otherwise that would probably be one way, to fetch the data from TOrders and inject the returned CustomerID’s into the IN clause of the main query.
But prehaps there is a similar way to “inject” an array of id’s into an IN clause or something?
you are using the order table in your where clause. this means that the query planner can’t start removing lines from the customer table early enough. have you tried joining in the order table earlier?