Sql Server right side restrictions on left join - sql-server

Please read it slowly. This isn't a dup.
Tables:
CREATE TABLE [dbo].[TEST] (
[TEST_ID] [integer] IDENTITY (1, 1) NOT NULL ,
....
[TEST_TYPE_ID] [char](1) NULL ,
....
)
CREATE TABLE [dbo].[TEST_A] (
[TEST_ID] [integer] NOT NULL ,
....
)
CREATE TABLE [dbo].[TEST_B] (
[TEST_ID] [integer] NOT NULL ,
....
)
Normally you would write:
select *
from dbo.TEST as t
left join dbo.TEST_A as ta on ta.TEST_ID = t.TEST_ID
left join dbo.TEST_B as tb on tb.TEST_ID = t.TEST_ID
...
However, Sql Server can save a lot of work - IF it knows that only some of table TEST's rows potentially join to TEST_A:
select *
from dbo.TEST as t
left join dbo.TEST_A as ta on t.TEST_TYPE_ID = 'A'
and ta.TEST_ID = t.TEST_ID
left join dbo.TEST_B as tb on t.TEST_TYPE_ID = 'B'
and tb.TEST_ID = t.TEST_ID
...
These queries return the exact same result. Adding TEST_TYPE_ID = X does not change the result.
Note: You CAN'T put the restriction on TEST_TYPE_ID in the where statement. That would change the number of rows returned.
My question is: In a left join if you place a restriction on the right side, will Sql Server use this information first? Order of operations is very important here. This is important when TEST and TEST_A are large, but only a few records join.
I have tested this, and the execution plan seems to indicate: no. It appears Sql Server first does a normal left join trying to join all the records in TEST to TEST_A, then it applies a "filter". However, I'm not certain I'm reading the execution plan correctly. If TEST_TYPE_ID = X is applied second, it is effectly a no-op. If TEST_TYPE_ID = X is applied first, it will limit the left join to only the rows that will actually join.
Note: My actual case looks very different. I have distilled the question down to this bare bones example to demonstrate the issue.

Related

The multi-part identifier "[column name]" could not be bound in UPDATE of TEMP Table

I am trying to create a stored procedure whereupon I input a (simple for now) query into a temp table, and then replace some of the data with data from a different table based on a key.
Here is the complete code:
CREATE PROCEDURE GetInquiryList
AS
BEGIN
SET NOCOUNT ON
IF OBJECT_ID('tempdb..#Inq ') IS NOT NULL
DROP TABLE #Inq
SELECT i.*,q.QuoteID INTO #Inq FROM Inquiries i left join Quotes q on i.InquiryId = q.InquiryId
WHERE i.YNDeleted = 0
--SELECT * FROM #Inq
UPDATE #Inq
SET j.InquiryCustomerName = c.CustomerName,
j.InquiryCustomerEmail = c.CustomerEmail,
j.InquiryCustomerPhone = c.CustomerPhone1,
j.InquiryBestTimetoCall = c.CustomerBestTimetoCall,
j.InquiryDay = c.customerDay,
j.InquiryNight = c.CustomerNight
SELECT c.CustomerName,
c.CustomerEmail,
c.CustomerPhone1,
c.CustomerBestTimetoCall,
c.customerDay,
c.CustomerNight
FROM Customers c
INNER JOIN #Inq j ON
j.InquiryCustomerID = c.CustomerID
SELECT * FROM #Inq
END
I get the following error:
Msg 4104, Level 16, State 1, Line 15 The multi-part identifier "j.InquiryCustomerName" could not be bound
I get this error for whatever column is placed first after the SET command.
Both query pieces of this work independently (the first select creating the temp table and the joined query at the bottom). The data returned is correct. I have tried using aliases (SELECT c.CustomerName AS Name, ...).
Originally, I used "#Inq i" in the second command, but changed to "j" out of an abundance of caution.
I have also run the command against the original table (substituting the Inquiry table for the temp table #Inq, and that fails as well).
Shortening it to this:
UPDATE #Inq
SET j.InquiryCustomerName = c.CustomerName,
j.InquiryCustomerEmail = c.CustomerEmail,
j.InquiryCustomerPhone = c.CustomerPhone1,
j.InquiryBestTimetoCall = c.CustomerBestTimetoCall,
j.InquiryDay = c.customerDay,
j.InquiryNight = c.CustomerNight
FROM Customers c
INNER JOIN #Inq j ON
j.InquiryCustomerID = c.CustomerID
I get a different error:
Msg 4104, Level 16, State 1, Line 15 The multi-part identifier "j.InquiryCustomerName" could not be bound
I'm sure it's probably something simple,(so simple that I can't find any references in any of my searches).
I'm sure it has something to do with the fact that you can't update the same instance of the table used in the join (I'm going to have to re-join again with a "k" alias). How do I do this?
data from the first query
data from the first query
data from the second select statement on the actual temp table
Here is what I updated the stored procedure to, which works exactly how I need it to:
SET NOCOUNT ON
IF OBJECT_ID('tempdb..#Inq ') IS NOT NULL
DROP TABLE #Inq
SELECT i.* INTO #Inq FROM (
select inquiries.InquiryId,
inquiries.InquiryDateReceived,
inquiries.InquiryCustomerID,
cust.CustomerName as InquiryCustomerName,
cust.CustomerEmail as InquiryCustomerEmail,
cust.CustomerPhone1 as InquiryCustomerPhone,
cust.CustomerBestTimeToCall as InquiryBestTimeToCall,
cust.CustomerDay as InquiryDay,
cust.CustomerNight as InquiryNight,
inquiries.InquiryServiceType,
inquiries.InquiryServiceID,
inquiries.InquiryTimeframe,
inquiries.InquiryProjectDescription,
inquiries.InquiryDateResponded,
inquiries.InquiryCustomerReply,
inquiries.YNMigrated,
inquiries.InquiryDateClosed,
inquiries.YNClosed,
inquiries.YNDeleted
from inquiries inner join dbo.Customers as cust
on inquiries.InquiryCustomerID = cust.CustomerID and inquiries.InquiryCustomerID > 0
UNION ALL
select inquiries.InquiryId,
inquiries.InquiryDateReceived,
inquiries.InquiryCustomerID,
InquiryCustomerName,
InquiryCustomerEmail,
InquiryCustomerPhone,
InquiryBestTimeToCall,
InquiryDay,
InquiryNight,
inquiries.InquiryServiceType,
inquiries.InquiryServiceID,
inquiries.InquiryTimeframe,
inquiries.InquiryProjectDescription,
inquiries.InquiryDateResponded,
inquiries.InquiryCustomerReply,
inquiries.YNMigrated,
inquiries.InquiryDateClosed,
inquiries.YNClosed,
inquiries.YNDeleted
from inquiries WHERE inquiries.InquiryCustomerID = 0
) i
select i.*, q.QuoteID
FROM #Inq i left join dbo.Quotes as q
on i.InquiryId = q.InquiryId
WHERE i.YNDeleted = 0
END
Just stop using this pattern without a really good reason. Here it only appears to create more work for the database engine with no obvious benefit. Your procedure - as posted - has trivially simple queries so why bother with the temp table and the update?
It is also time to start learning and using best practices. Terminate EVERY statement - eventually it will be required. Does order of the rows in your resultset matter? Usually it does and that is only guaranteed when that resultset is produced by a query that includes an ORDER BY clause.
As a developing/debugging short cut, you can harness the power of CTEs to help you build a working query. In this case, you can "stuff" your first query into a CTE and then simply join the CTE to Customers and "adjust" the columns you need in that resultset.
WITH inquiries as (
select inq.*, qt.QuoteID
FROM dbo.Inquiries as inq left join dbo.Quotes as qt
on inq.InquiryId = qt.InquiryId
WHERE inq.YNDeleted = 0
)
select inquiries.<col>,
...,
cust.CustomerName as "InquiryCustomerName",
...
from inquiries inner (? guessing) dbo.Customers as cust
on inquiries.InquiryCustomerID = cust.CustomerID
order by ...
;
Schema names added as best practice. Listing the columns you actually need in your resultset is another best practice. Note I did not do that for the query in the CTE but you should. You can choose to create aliases for your resultset columns as needed. I listed one example that corresponds to your UPDATE attempt.
It is odd and very suspicious that all of the columns you intended to UPDATE exist in the Inquiries table. Are you certain you need to do that at all? Do they actually differ from the related columns in the Customer table? Also odd that the value 0 exists in InquiryCustomerID - suggesting you might have not a FK to enforce the relationship. Perhaps that means you need to outer join rather than inner join (as I wrote). If an outer join is needed, then you will need to use CASE expressions to "choose" which value (the CTE value or the Customer value) to use for those columns.
After learning a lot more about how things get bound to models, and how to further use sql, here is what my stored procedure looks like:
ALTER PROCEDURE [dbo].[GetInquiryList]
#InquiryID int = 0
AS
BEGIN
SET NOCOUNT ON
select i.InquiryId,
i.InquiryDateReceived,
i.InquiryCustomerID,
InquiryCustomerName =
CASE i.InquiryCustomerID
WHEN 0 THEN i.InquiryCustomerName
ELSE c.CustomerName
END,
InquiryCustomerEmail =
CASE i.InquiryCustomerID
WHEN 0 THEN i.InquiryCustomerEmail
ELSE c.CustomerEmail
END,
InquiryCustomerPhone =
CASE i.InquiryCustomerID
WHEN 0 THEN i.InquiryCustomerPhone
ELSE c.CustomerPhone1
END,
InquiryBestTimetoCall =
CASE i.InquiryCustomerID
WHEN 0 THEN i.InquiryBestTimetoCall
ELSE c.CustomerBestTimetoCall
END,
InquiryDay =
CASE i.InquiryCustomerID
WHEN 0 THEN i.InquiryDay
ELSE c.CustomerDay
END,
InquiryNight =
CASE i.InquiryCustomerID
WHEN 0 THEN i.InquiryNight
ELSE c.CustomerNight
END,
i.InquiryServiceType,
i.InquiryServiceID,
i.InquiryTimeframe,
i.InquiryProjectDescription,
i.InquiryDateResponded,
i.InquiryCustomerReply,
i.YNMigrated,
i.InquiryDateClosed,
i.YNClosed,
i.YNDeleted, ISNULL(q.QuoteId,0) AS Quoteid
FROM dbo.Inquiries i
LEFT JOIN dbo.Quotes q ON i.InquiryId = q.InquiryId
LEFT JOIN dbo.Customers c ON i.InquiryCustomerID = c.CustomerId
WHERE i.YNDeleted = 0
END
I'm sure there are additional enhancements that could be made, but avoiding the union is a big savings. Thanks, everyone.

Why is using Table Spool slower than not?

There are two similiar sqls running in sql server,in which the table TBSFA_DAT_CUST has millons rows and no constraint(no index and primary key),
the other two has just a few rows and normal primary key:
s for slower one:
SELECT A.CUST_ID, C.CUST_NAME, A.xxx --and several specific columns
FROM TBSFA_DAT_ORD_LIST A JOIN VWSFA_ORG_EMPLOYEE B ON A.EMP_ID = B.EMP_ID
LEFT JOIN TBSFA_DAT_CUST C ON A.CUST_ID = B.CUST_ID
JOIN VWSFA_ORG_EMPLOYEE D ON A.REVIEW_ID = D.EMP_ID
WHERE ISNULL(A.BATCH_ID, '') != ''
execution plan of slower one
f for faster one:
SELECT *
FROM TBSFA_DAT_ORD_LIST A JOIN VWSFA_ORG_EMPLOYEE B ON A.EMP_ID = B.EMP_ID
LEFT JOIN TBSFA_DAT_CUST C ON A.CUST_ID = B.CUST_ID
JOIN VWSFA_ORG_EMPLOYEE D ON A.REVIEW_ID = D.EMP_ID
WHERE ISNULL(A.BATCH_ID, '') != ''
execution plan of faster one
f(above 0.6s) is much faster than s(above 4.6s).
Otherwise,I found two ways to make s fast as f:
1.Add constaint and primary key in table TBSFA_DAT_CUST.CUST_ID;
2.Specific more than 61 columns of table TBSFA_DAT_CUST(totally 80 columns).
My question is why sql optimizer uses Table Spool when I specific columns in SELECT clause rather than '*',and why is using Table Spool one executes slower?
My question is about sql-servertable-spool
In the slower query you are limiting your result set to specific columns. Since this is an un-indexed un constrained table the optimizer is creating a temporary table from the original table scan with only the specific columns required. It is then running through the nested loop operator on the temporary table. When it knows its going to need every column on the table (Select *) it can run the nested loop operator directly off the table scan because the result set of the scan will be joined in full to the top table.
Outside of that your query has a couple other possible problems:
LEFT JOIN TBSFA_DAT_CUST C ON A.CUST_ID = B.CUST_ID
you aren't joining to anything here, you are joining the entire table to every record. Did mean a.cust_id = c.cust_id or b.cust_id = c.cust_id or a.cust_id = c.cust_id and b.cust_id = c.cust_id?
Also, this function in the where clause is pointless and can degrade performance:
WHERE ISNULL(A.BATCH_ID, '') != ''
change it to:
WHERE A.BATCH_ID is not null and A.Batch_ID <> ''

RIGHT\LEFT Join does not provide null values without condition

I have two tables one is the lookup table and the other is the data table. The lookup table has columns named cycleid, cycle. The data table has SID, cycleid, cycle. Below is the structure of the tables.
If you check the data table, the SID may have all the cycles and may not have all the cycles. I want to output the SID completed as well as missed cycles.
I right joined the lookup table and retrieved the missing as well as completed cycles. Below is the query I used.
SELECT TOP 1000 [SID]
,s4.[CYCLE]
,s4.[CYCLEID]
FROM [dbo].[data] s3 RIGHT JOIN
[dbo].[lookup_data] s4 ON s3.CYCLEID = s4.CYCLEID
The query is not displaying me the missed values when I query for all the SID's. When I specifically query for a SID with the below query i am getting the correct result including the missed ones.
SELECT TOP 1000 [SID]
,s4.[CYCLE]
,s4.[CYCLEID]
FROM [dbo].[data] s3 RIGHT JOIN [dbo].[lookup_data] s4
ON s3.CYCLEID = s4.CYCLEID
AND s3.SID = 101002
ORDER BY [SID], s4.[CYCLEID]
As I am supplying this query into tableau I cannot provide the sid value in the query. I want to return all the sid's and from tableau I will be do the rest of the things.
The expected output that i need is as shown below.
I wrote a cross join query like below to acheive my expected output
SELECT DISTINCT
tab.CYCLEID
,tab.SID
,d.CYCLE
FROM ( SELECT d.SID
,d.[CYCLE]
,e.CYCLEID
FROM ( SELECT e.sid
,e.CYCLE
FROM [db_temp].[dbo].[Sheet3$] e
) d
CROSS JOIN [db_temp].[dbo].[Sheet4$] e
) tab
LEFT OUTER JOIN [db_temp].[dbo].[Sheet3$] d
ON d.CYCLEID = tab.CYCLEID
AND d.SID = tab.SID
ORDER BY tab.SID
,tab.CYCLEID;
However I am not able to use this query for more scenarios as my data set have nearly 20 to 40 columns and i am having issues when i use the above one.
Is there any way to do this in a simpler manner with only left or right join itself? I want the query to return all the missing values and the completed values for the all the SID's instead of supplying a single sid in the query.
You can create a master table first (combine all SID and CYCLE ID), then right join with the data table
;with ctxMaster as (
select distinct d.SID, l.CYCLE, l.CYCLEID
from lookup_data l
cross join data d
)
select d.SID, m.CYCLE, m.CYCLEID
from ctxMaster m
left join data d on m.SID = d.SID and m.CYCLEID = d.CYCLEID
order by m.SID, m.CYCLEID
Fiddle
Or if you don't want to use common table expression, subquery version:
select d.SID, m.CYCLE, m.CYCLEID
from (select distinct d.SID, l.CYCLE, l.CYCLEID
from lookup_data l
cross join data d) m
left join data d on m.SID = d.SID and m.CYCLEID = d.CYCLEID
order by m.SID, m.CYCLEID

Referencing Results of Multiple Temporary Tables in SQL Server

I’m using SQL Server 2008
I have joins written something like the following, where the first join is encapsulated in a ‘With as’ statement so that I can name the output table as ‘A’ and then reference the ‘A’ resulting table in the next select and Join seen beneath it.
This works perfectly fine. What I would like to do then is reference that second table for another select statement and join, but when I try to wrap it in a ‘With as’ statement as well, the editor does not accept it as legitimate syntax for the second instance of 'With as'.
How can I subset resulting tables to reference in further select and join statements? I do not have permission to write to the database, so I can not create permanent tables in the database.
Thank you.
With A as
(
SELECT POL.[COMPANY_CODE]
,POL.[POLICY_NUMBER]
,POL.[STATUS_CODE]
,POL.ORIG_CLIENT_NUM
,TA.LINE
FROM [SamsReporting].[dbo].[POLICY] POL
Left join [SamsReporting].[dbo].[Transact] TA
ON TA.POLICY_NUMBER = POL.POLICY_NUMBER and TA.BASE_Account = 'B'
)
Select PM.POLICY_NUMBER
,A.[COMPANY_CODE]
,A.[POLICY_NUMBER]
,A.[Policy Status]
,eApp.SourceCode
From A
Left Join Web.dbo.Pmetrics PM on A.POLICY_NUMBER=PM.POLICY_NUMBER
Left Outer Join DDP.pol.eAppStaging eApp
on A.POLICY_NUMBER=eApp.PolicyNumber
where eApp.SourceCode = 'HAQ' or eApp.SourceCode = 'PLS'
Common Table Expressions (CTEs) can build upon each other as you would like. For example, you can do this:
WITH CTE1 AS (SELECT * FROM Table 1)
, CTE2 AS (SELECT * FROM CTE1)
, CTE3 AS (SELECT * FROM CTE2)
You only need the WITH statement for the first CTE. After that just use the CTE name, as in my example.
Hope that helps,
Ash
Sounds like a syntax issue to me. Google CTE (Common Table Expression) and review some examples of how they are formed.
With A as
(SELECT POL.[COMPANY_CODE]
,POL.[POLICY_NUMBER]
,POL.[STATUS_CODE]
,POL.ORIG_CLIENT_NUM
,TA.LINE
FROM [SamsReporting].[dbo].[POLICY] POL
Left join [SamsReporting].[dbo].[Transact] TA
ON TA.POLICY_NUMBER = POL.POLICY_NUMBER and TA.BASE_Account = 'B'),
B as (
Select PM.POLICY_NUMBER
,A.[COMPANY_CODE]
,A.[POLICY_NUMBER]
,A.[Policy Status]
,eApp.SourceCode
From A
Left Join Web.dbo.Pmetrics PM on A.POLICY_NUMBER=PM.POLICY_NUMBER
Left Outer Join DDP.pol.eAppStaging eApp
on A.POLICY_NUMBER=eApp.PolicyNumber
where eApp.SourceCode = 'HAQ' or eApp.SourceCode = 'PLS')
Select *
From B -- inner join some table
where some condition = 1

SQL Server 2008 Stored Procedure Performance issue

Hi I have a Stored Procedure
ALTER PROCEDURE [dbo].[usp_EP_GetTherapeuticalALternates]
(
#NDCNumber CHAR(11) ,
#patientid INT ,
#pbmid INT
)
AS
BEGIN
TRUNCATE TABLE TempTherapeuticAlt
INSERT INTO TempTherapeuticAlt
SELECT --PR.ProductID AS MedicationID ,
NULL AS MedicationID ,
PR.ePrescribingName AS MedicationName ,
U.Strength AS MedicationStrength ,
FRM.FormName AS MedicationForm ,
PR.DEAClassificationID AS DEASchedule ,
NULL AS NDCNumber
--INTO #myTemp
FROM DatabaseTwo.dbo.Product PR
JOIN ( SELECT MP.MarketedProductID
FROM DatabaseTwo.dbo.Therapeutic_Concept_Tree_Specific_Product TCTSP
JOIN DatabaseTwo.dbo.Marketed_Product MP ON MP.SpecificProductID = TCTSP.SpecificProductID
JOIN ( SELECT TCTSP.TherapeuticConceptTreeID
FROM DatabaseTwo.dbo.Marketed_Product MP
JOIN DatabaseTwo.dbo.Therapeutic_Concept_Tree_Specific_Product TCTSP ON MP.SpecificProductID = TCTSP.SpecificProductID
JOIN ( SELECT
PR.MarketedProductID
FROM
DatabaseTwo.dbo.Package PA
JOIN DatabaseTwo.dbo.Product PR ON PA.ProductID = PR.ProductID
WHERE
PA.NDC11 = #NDCNumber
) PAPA ON MP.MarketedProductID = PAPA.MarketedProductID
) xxx ON TCTSP.TherapeuticConceptTreeID = xxx.TherapeuticConceptTreeID
) MPI ON PR.MarketedProductID = MPI.MarketedProductID
JOIN ( SELECT P.ProductID ,
O.Strength ,
O.Unit
FROM DatabaseTwo.dbo.Product AS P
INNER JOIN DatabaseTwo.dbo.Marketed_Product
AS M ON P.MarketedProductID = M.MarketedProductID
INNER JOIN DatabaseTwo.dbo.Specific_Product
AS S ON M.SpecificProductID = S.SpecificProductID
LEFT OUTER JOIN DatabaseTwo.dbo.OrderableName_Combined
AS O ON S.SpecificProductID = O.SpecificProductID
GROUP BY P.ProductID ,
O.Strength ,
O.Unit
) U ON PR.ProductID = U.ProductID
JOIN ( SELECT PA.ProductID ,
S.ScriptFormID ,
F.Code AS NCPDPScriptFormCode ,
S.FormName
FROM DatabaseTwo.dbo.Package AS PA
INNER JOIN DatabaseTwo.dbo.Script_Form
AS S ON PA.NCPDPScriptFormCode = S.NCPDPScriptFormCode
INNER JOIN DatabaseTwo.dbo.FormCode AS F ON S.FormName = F.FormName
GROUP BY PA.ProductID ,
S.ScriptFormID ,
F.Code ,
S.FormName
) FRM ON PR.ProductID = FRM.ProductID
WHERE
( PR.OffMarketDate IS NULL )
OR ( PR.OffMarketDate = '' )
OR (PR.OffMarketDate = '1899-12-30 00:00:00.000')
OR ( PR.OffMarketDate <> '1899-12-30 00:00:00.000'
AND DATEDIFF(dd, GETDATE(),PR.OffMarketDate) > 0
)
GROUP BY PR.ePrescribingName ,
U.Strength ,
FRM.FormName ,
PR.DEAClassificationID
-- ORDER BY pr.ePrescribingName
SELECT LL.ProductID AS MedicationID ,
temp.MedicationName ,
temp.MedicationStrength ,
temp.MedicationForm ,
temp.DEASchedule ,
temp.NDCNumber ,
fs.[ReturnFormulary] AS FormularyStatus ,
copay.CopaTier ,
copay.FirstCopayTerm ,
copay.FlatCopayAmount ,
copay.PercentageCopay ,
copay.PharmacyType,
dbo.udf_EP_GetBrandGeneric(LL.ProductID) AS BrandGeneric
FROM TempTherapeuticAlt temp
OUTER APPLY ( SELECT TOP 1
ProductID
FROM DatabaseTwo.dbo.Product
WHERE ePrescribingName = temp.MedicationName
) AS LL
OUTER APPLY [dbo].[udf_EP_tbfGetFormularyStatus](#patientid,
LL.ProductID,
#pbmid) AS fs
OUTER APPLY ( SELECT TOP 1
*
FROM udf_EP_CopayDetails(LL.ProductID,
#PBMID,
fs.ReturnFormulary)
) copay
--ORDER BY LL.ProductID
TRUNCATE TABLE TempTherapeuticAlt
END
On my dev server I have data of 63k in each table
so this procedure took about 30 seconds to return result.
On my Production server, it is timing out, or taking >1 minute.
I am wondering my production server tables are full with 1400 millions of records,
can this be a reason.
if so what can be done, I have all required indexes on tables.
any help would be greatly appreciated.
thanks
Execution Plan
http://www.sendspace.com/file/hk8fao
Major Leakage
OUTER APPLY [dbo].[udf_EP_tbfGetFormularyStatus](#patientid,
LL.ProductID,
#pbmid) AS fs
Some strategies that may help:
Remove the first ORDER BY statement, those are killer on complex queries shouldn't be necessary.
Use CTEs to break the query into smaller pieces that can be individually addressed.
Reduce the nesting in the first set of JOINs
Extract the second and third set of joins (the GROUPED ones) and insert those into a temporary indexed table before joining and grouping everything.
You did not include the definition for function1 or function2 -- custom functions are often a place where performance issues can hide.
Without seeing the execution plan, it's difficult to see where the particular problems may be.
You have a query that selects data from 4 or 5 tables , some of them multiple times. It's really hard to say how to improve without deep analysis of what you are trying to achieve and what table structure actually is.
Data size is definitely an issue; I think it's quite obvious that the more data has to be processed, the longer query will take. Some general advices... Run the query directly and check execution plan. It may reveal bottlenecks. Then check if statistics is up to date. Also, review your tables, partitioning may help a lot in some cases. In addition, you can try altering tables and create clustered index not on PK (as it's done by default unless otherwise specified), but on other column[s] so your query will benefit from certain physical order of records. Note : do it only if you are absolutely sure what you are doing.
Finally, try refactoring your query. I have a feeling that there is a better way to get desired results (sorry, without understanding of table structure and expected results I cannot tell exact solution, but multiple joins of the same tables and bunch of derived tables don't look good to me)

Resources