Improve the speed of this string_agg? - sql

I have data of the following shape:
BOM -- 500 rows, 4 cols
PartProject -- 2.6mm rows, 4 cols
Project -- 1000 rows, 5 cols
Part -- 200k rows, 18 cols
Yet when I try to do string_agg, my code will take me well over 10 minutes to execute on 500 rows. How can I improve this query (the data is not available).
select
BOM.*,
childParentPartProjectName
into #tt2 -- tt for some testing
from #tt1 AS BOM -- tt for some testing
-- cross applys for string agg many to one
CROSS APPLY (
SELECT childParentPartProjectName = STRING_AGG(PROJECT_childParentPart.NAME, ', ') WITHIN GROUP (ORDER BY PROJECT_childParentPart.NAME)
FROM (
SELECT DISTINCT PROJECT3.NAME
FROM [dbo].[Project] PROJECT3
LEFT JOIN [dbo].[Part] P3 on P3.ITEM_NUMBER = BOM.childParentPart
LEFT JOIN [dbo].[PartProject] PP3 on PP3.SOURCE_ID = P3.ID
WHERE PP3.RELATED_ID = PROJECT3.ID and P3.CURRENT = 1
) PROJECT_childParentPart ) PROJECT3

The subquery (within a subquery) you have has a code "smell" to it that it's been written with intention, but not correctly.
Firstly you have 2 LEFT JOINs in the subquery, however, both the tables aliased as P3 and PP3 are required to have a non-NULL value; that is impossible if no related row is found. This means the JOINs are implicit INNER JOINs.
Next you have a DISTINCT against a single column when SELECTing from multiple tables; this seems wrong. DISTINCT is very expensive and the fact you are using it implies that either NAME is not unique or that due to your implicit INNER JOINs you are getting duplicate rows. I assume it's the latter. As a results, very likely you should actually be using an EXISTS, not LEFT JOINs INNER JOINs.
The following is very much a guess, but I suspect it will be more performant.
SELECT BOM.*, --Replace this with an explicit list of the columns you need
SA.childParentPartProjectName
INTO #tt2
FROM #tt1 BOM
CROSS APPLY (SELECT STRING_AGG(Prj.NAME, ', ') WITHIN GROUP (ORDER BY Prj.NAME) AS childParentPartProjectName
FROM dbo.Project Prj --Don't use an alias that is longer than the object name
WHERE EXISTS (SELECT 1
FROM dbo.Part P
JOIN dbo.PartProject PP ON P.ID = PP.SOURCE_ID
WHERE PP.Related_ID = Prg.ID
AND P.ITEM_NUMBER = BOM.childParentPart
AND P.Current = 1)) SA;

Related

Two SELECTS return different counts, but all types of JOINS return same NUMBER of Rows, <> to either SELECT

(Using SSMS v15 against a pair of Oracle v19 dbs)
SELECT against Table1 in new management system's db1 returns 312 depreciation trxns for the month.
SELECT against View1 in old management system's db2 returns 311 depreciation transactions for the month.
Discrepancies were expected between home-brewed system and new vended one, but finding them by eyeballing results from each result set is making my eyes hurt, and I have about 4500 rows of various types of transactions to validate...each of the next three months.
I would like JOINs to show me which ones on one side are missing on the other.
And these discrepancies might go both ways.
So I'd like a statement of LEFT JOIN, showing all 312 rows from first SELECT, and NULLS for the rows that are missing from the other.
AND I'd like a statement of RIGHT JOIN, showing the 311 rows from second SELECT, and NULLS for any rows missing from the other.
However, every JOIN returns 310 rows.
I have not been through all 310 to see if the same 310 have been returned for all four JOIN types, but it's 310 rows, every time.
Not a NULL row from either side.
Possibilities:
SQL Server Mgmt Studio not playing well with Oracle?
VIEW causing some side effects?
Operator error?
Any help would be appreciated.
Thanks
select * -- = 312 rows
from Database1.ACCT_EOM_TRANSACTIONS
where period_id = '092021'
and trans_type in ('DEPRECIATION', 'OTHER FIXED 3')
order by trans_type, eq_equip_no
select * -- = 311 rows
from Database2.V_FTS_SUM
where bill_month = 9 and bill_year = 2021
and trans_type = 'DEPR'
order by trans_type, veh_ref_no
Select E.*, V.*
from Database1.ACCT_EOM_TRANSACTIONS E
JOIN Database2.V_FTS_SUM V --INNER, RIGHT OUTER, LEFT OUTER, JOIN = 310 rows
ON E.eq_equip_no = V.veh_ref_no
where E.period_id = '092021'
and E.trans_type in ('DEPRECIATION', 'OTHER FIXED 3')
and V.bill_month = 9 and V.bill_year = 2021
and V.trans_type = 'DEPR'
order by E.eq_equip_no
Your WHERE clause was breaking the outer join behavior.
To find mismatches in both left and right side, use a FULL [OUTER] JOIN, like this:
WITH cte1 AS (
select * -- = 312 rows
from ACCT_EOM_TRANSACTIONS
where period_id = '092021'
and trans_type in ('DEPRECIATION', 'OTHER FIXED 3')
)
, cte2 AS (
select * -- = 311 rows
from V_FTS_SUM
where bill_month = 9 and bill_year = 2021
and trans_type = 'DEPR'
)
SELECT *
FROM cte1 FULL JOIN cte2
ON cte1.eq_equip_no = cte2.veh_ref_no
WHERE cte1.eq_equip_no IS NULL
OR cte2.veh_ref_no IS NULL
;
Fiddle: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=4c68480cdfc5b9d06d5d70ade66235e3

Union colums sql server vertically

I have two tables that has the same columns numbers and names
and I want to join its columns.
I have this table called inicial_dolares
And have this other table called inicial_cordobas
I am looking for this result
I've tried do this with joins but does not work.
the common column is id_arqueo is a integer.
and I tried this SQL union but vertically
but does not work.
SELECT IC.id_arqueo,
IC.id_detalle,
IC.descripcion,
IC.denominacion,
IC.cantidad,
IC.total,
ID.id_arqueo,
ID.id_detalle,
ID.descripcion,
ID.denominacion,
ID.cantidad,
ID.total
FROM dbo.inicial_cordobas IC
LEFT JOIN dbo.inicial_dolares ID
ON ID.id_arqueo = IC.id_arqueo
--this query returns to me 168 rows because the join looks for coincidence and
--one table has 14 and the other has 12 rows.
you need row_number() to join your 2nd table.
SELECT IC.id_arqueo,
IC.id_detalle,
IC.descripcion,
IC.denominacion,
IC.cantidad,
IC.total,
ID.id_arqueo,
ID.id_detalle,
ID.descripcion,
ID.denominacion,
ID.cantidad,
ID.total
FROM
(select row_number() over (order by id_detalle) rn, * from dbo.inicial_cordobas) IC
LEFT JOIN
(select row_number() over (order by id_detalle) rn , * from dbo.inicial_dolares) ID
ON ID.id_arqueo = IC.id_arqueo and IC.rn = ID.rn
The id_detalle columns also have a relationship that can be expressed mathematically and used:
SELECT IC.id_arqueo,
IC.id_detalle,
IC.descripcion,
IC.denominacion,
IC.cantidad,
IC.total,
ID.id_arqueo,
ID.id_detalle,
ID.descripcion,
ID.denominacion,
ID.cantidad,
ID.total
FROM dbo.inicial_cordobas IC
LEFT JOIN dbo.inicial_dolares ID
ON ID.id_arqueo = IC.id_arqueo
AND ic.id_detalle + 14 = id.id_detalle
The id_detalle in ic is always 14 less than the id_detalle in id so we can make a joint condition where we add 14 to the value in ic and then relate it to the value in id
This is possibly an important learning point that joins don't have to always take the form tableA.columnA = tableB.columnB. Anything that can be expressed as a truth may work as a join condition, including formulas and maths operations on either side of the =

JOIN on multiple fields between 2 Tables. How to determine which field caused the join to fail?

I have the following SQL Server query:
SELECT TOP (100) PERCENT
dbo.cct_prod_plc_log_data.wc,
dbo.cct_prod_plc_log_data.loc,
dbo.cct_prod_plc_log_data.ord_no,
dbo.cct_prod_plc_log_data.ser_lot_no,
dbo.cct_prod_plc_log_data.line,
ISNULL(dbo.imlsmst_to_sfdtlfil.ItemNo, '') AS ItemNo,
ISNULL(dbo.imlsmst_to_sfdtlfil.BldSeqNo, '') AS BldSeqNo,
ISNULL(dbo.imlsmst_to_sfdtlfil.BldOrdNo, '') AS BldOrdNo,
ISNULL(dbo.imlsmst_to_sfdtlfil.StringItemNo, '') AS StringItemNo,
ISNULL(dbo.imlsmst_to_sfdtlfil.StringSerLotNo, '') AS StringSerLotNo,
MAX(dbo.cct_prod_plc_log_data.InsertDateTime) AS LatestDateTime,
MIN(ISNULL(dbo.cct_prod_plc_log_data.erp_transaction_id, 0)) AS MinimumErpID,
ISNULL(dbo.imlsmst_to_sfdtlfil.QtyOnHand, 0) AS QtyOnHand
FROM
dbo.cct_prod_plc_log_data
LEFT OUTER JOIN dbo.imlsmst_to_sfdtlfil
ON dbo.cct_prod_plc_log_data.ser_lot_no = dbo.imlsmst_to_sfdtlfil.SerLotNo
AND dbo.cct_prod_plc_log_data.ord_no = dbo.imlsmst_to_sfdtlfil.OrderNo
AND dbo.cct_prod_plc_log_data.line = dbo.imlsmst_to_sfdtlfil.Bin
WHERE
( dbo.cct_prod_plc_log_data.erp_transaction_id < 3 OR dbo.cct_prod_plc_log_data.erp_transaction_id IS NULL )
AND (dbo.cct_prod_plc_log_data.wc <> '')
AND (dbo.cct_prod_plc_log_data.loc <> '')
AND (dbo.cct_prod_plc_log_data.line <> '')
GROUP BY
dbo.cct_prod_plc_log_data.wc,
dbo.cct_prod_plc_log_data.loc,
dbo.cct_prod_plc_log_data.ord_no,
dbo.cct_prod_plc_log_data.ser_lot_no,
dbo.cct_prod_plc_log_data.line,
dbo.imlsmst_to_sfdtlfil.ItemNo,
dbo.imlsmst_to_sfdtlfil.BldSeqNo,
dbo.imlsmst_to_sfdtlfil.BldOrdNo,
dbo.imlsmst_to_sfdtlfil.StringItemNo,
dbo.imlsmst_to_sfdtlfil.StringSerLotNo,
dbo.imlsmst_to_sfdtlfil.QtyOnHand
ORDER BY dbo.cct_prod_plc_log_data.ord_no DESC
It contains a Left Outer Join between the two tables on 3 fields. Based on the current construction if any of the 3 Joined fields in the right table (dbo.imlsmst_to_sfdtlfil) are null or missing then the fields in the left query should return null.
How do I determine which of the 3 fields is the field that caused the join to fail? I would like to differentiate these from each other. Thanks.
(Ex. ser_lot_no and ord_no exists but bin is null vs bin and ord_no exist but ser_lot_no is null. )
Change it for an inner join and comment out all but one of the conditions, then uncomment them one at a time until the data disappears again - that's the faulty condition. If there was no data even with just one condition, that's the faulty condition:
SELECT
c.wc,
c.loc,
c.ord_no,
c.ser_lot_no,
c.line,
COALESCE(i.ItemNo, '') AS ItemNo,
COALESCE(i.BldSeqNo, '') AS BldSeqNo,
COALESCE(i.BldOrdNo, '') AS BldOrdNo,
COALESCE(i.StringItemNo, '') AS StringItemNo,
COALESCE(i.StringSerLotNo, '') AS StringSerLotNo,
MAX(c.InsertDateTime) AS LatestDateTime,
MIN(COALESCE(c.erp_transaction_id, 0)) AS MinimumErpID,
COALESCE(i.QtyOnHand, 0) AS QtyOnHand
FROM
dbo.cct_prod_plc_log_data c
INNER JOIN dbo.imlsmst_to_sfdtlfil i
ON
c.ser_lot_no = i.SerLotNo
--AND c.ord_no = i.OrderNo
--AND c.line = i.Bin
WHERE
( c.erp_transaction_id < 3 OR c.erp_transaction_id IS NULL )
AND (c.wc <> '')
AND (c.loc <> '')
AND (c.line <> '')
GROUP BY
c.wc,
c.loc,
c.ord_no,
c.ser_lot_no,
c.line,
COALESCE(i.ItemNo, ''),
COALESCE(i.BldSeqNo, '')
COALESCE(i.BldOrdNo, '')
COALESCE(i.StringItemNo, '')
COALESCE(i.StringSerLotNo, '')
COALESCE(i.QtyOnHand, 0)
ORDER BY c.ord_no DESC
Using INNER JOIN is more obvious than OUTER JOIN as most query tools give a row count and it's easier to see the row count changing from 99990 to 100000 than it is to eyeball 100000 rows looking for 10 that are null when they shouldn't be
If you have more than 2 tables, comment out your select block, put a *, and all but 2 tables:
SELECT *
/* columns,list,here,blah,blah */
FROM
table1
JOIN table2 ON ...
--JOIN table3 on ...
--JOIN table4 on ...
Run it, get the expected number of rows, then proceed uncommenting more and more tables. If at any point your row count changes unexpectedly (more when you expected less, or less when you expected more) investigate.
If the row count increases, it's probably a cartesian product and should be resolved by adding extra join conditions, not by whacking a DISTINCT in
Other top tips:
Use COALESCE rather than ISNULL; improve your database cross skilling
Alias tables and use the alias name, rather than repeating the schema and column name everywhere
GROUP BY the coalesced result rather than the column, if you're using a DB that draws a distinction between empty string and null string, otherwise you'll end up with two rows in your results when you expect 1
Edit: You said:
Thank you for the insight and tips. However, my problem was more so a question on how to incorporate the information on which field was causing the join to fail as a permanent addition rather than a one time audit. Any insight for that? –
And I say:
You can't feasibly do this, the database cannot tell you "which field" isn't working out because most of them that aren't working out. To see what I mean, run this:
SELECT
-- replace .id with the name of the pk column
CONCAT('Cannot join c[', c.id, '] to i[', i.id, '] because: ',
CASE
WHEN COALESCE(c.ser_lot_no, 'null') != COALESCE(i.SerLotNo, 'null ') THEN 'c.ser_lot_no != i.SerLotNo, '
END,
CASE
WHEN COALESCE(c.ord_no, 'null') != COALESCE(i.OrderNo, 'null ') THEN 'c.ord_no != i.OrderNo, '
END,
CASE
WHEN COALESCE(c.line, 'null') != COALESCE(i.Bin, 'null ') THEN 'c.line != i.Bin, '
END
)
FROM
dbo.cct_prod_plc_log_data c
CROSS JOIN dbo.imlsmst_to_sfdtlfil i
It asks the database to join every row to every other row and then look at the values on the row and work out whether it can be joined or not.. If Table c has 1000 rows and table i has 2000 rows (and each row in c matches at most 2 rows in i), you'll get a result set of 2 million rows, 1998000 of which are "can't join this row to that row because..."
A.id
1
2
3
B.id
3
4
5
The only row from A that joins with B is "3", and even then "3" from A doesn't join with 4 or 5 from B, and 3 from B doesn't join with 1 or 2 from A. For your single set of matched rows, you have 8 complaints that the rows don't match (3x3 rows total, minus one match)
So no, you can't feasibly ask a database to tell you which rows from this table didn't match which rows from that table because of condition X, because the answer is "nearly all of them didn't match" and "all" could be hundreds of millions
It gets marginally more feasible if you have some join columns that should work out all the time, and others that sometimes don't:
SELECT CASE WHEN a.something != b.other THEN 'this row would fail because something != other' END
FROM a JOIN b ON a.id = b.id --and a.something = b.other
But think about it for a second; relational databases are centered around the idea that data is related, and you can even enforce it with constraints: "don't allow row X to be inserted here unless it has an A and a B and a C value that is present in this other table's D and E and F columns"
That's what you should be using to ensure your joins work out (relational integrity), not allowing any old crap into the database and then trying to work out which rows might have joined to which other rows if only there wasn't some typo in column A that meant it didn't quite match up with D, even though B/C matched with E/F ..

SQL query: Iterate over values in table and use them in subquery

I have a simple SQL table containing some values, for example:
id | value (table 'values')
----------
0 | 4
1 | 7
2 | 9
I want to iterate over these values, and use them in a query like so:
SELECT value[0], x1
FROM (some subquery where value[0] is used)
UNION
SELECT value[1], x2
FROM (some subquery where value[1] is used)
...
etc
In order to get a result set like this:
4 | x1
7 | x2
9 | x3
It has to be in SQL as it will actually represent a database view. Of course the real query is a lot more complicated, but I tried to simplify the question while keeping the essence as much as possible.
I think I have to select from values and join the subquery, but as the value should be used in the subquery I'm lost on how to accomplish this.
Edit: I oversimplified my question; in reality I want to have 2 rows from the subquery and not only one.
Edit 2: As suggested I'm posting the real query. I simplified it a bit to make it clearer, but it's a working query and the problem is there. Note that I have hardcoded the value '2' in this query two times. I want to replace that with values from a different table, in the example table above I would want a result set of the combined results of this query with 4, 7 and 9 as values instead of the currently hardcoded 2.
SELECT x.fantasycoach_id, SUM(round_points)
FROM (
SELECT DISTINCT fc.id AS fantasycoach_id,
ffv.formation_id AS formation_id,
fpc.round_sequence AS round_sequence,
round_points,
fpc.fantasyplayer_id
FROM fantasyworld_FantasyCoach AS fc
LEFT JOIN fantasyworld_fantasyformation AS ff ON ff.id = (
SELECT MAX(fantasyworld_fantasyformationvalidity.formation_id)
FROM fantasyworld_fantasyformationvalidity
LEFT JOIN realworld_round AS _rr ON _rr.id = round_id
LEFT JOIN fantasyworld_fantasyformation AS _ff ON _ff.id = formation_id
WHERE is_valid = TRUE
AND _ff.coach_id = fc.id
AND _rr.sequence <= 2 /* HARDCODED USE OF VALUE */
)
LEFT JOIN fantasyworld_FantasyFormationPlayer AS ffp
ON ffp.formation_id = ff.id
LEFT JOIN dbcache_fantasyplayercache AS fpc
ON ffp.player_id = fpc.fantasyplayer_id
AND fpc.round_sequence = 2 /* HARDCODED USE OF VALUE */
LEFT JOIN fantasyworld_fantasyformationvalidity AS ffv
ON ffv.formation_id = ff.id
) x
GROUP BY fantasycoach_id
Edit 3: I'm using PostgreSQL.
SQL works with tables as a whole, which basically involves set operations. There is no explicit iteration, and generally no need for any. In particular, the most straightforward implementation of what you described would be this:
SELECT value, (some subquery where value is used) AS x
FROM values
Do note, however, that a correlated subquery such as that is very hard on query performance. Depending on the details of what you're trying to do, it may well be possible to structure it around a simple join, an uncorrelated subquery, or a similar, better-performing alternative.
Update:
In view of the update to the question indicating that the subquery is expected to yield multiple rows for each value in table values, contrary to the example results, it seems a better approach would be to just rewrite the subquery as the main query. If it does not already do so (and maybe even if it does) then it would join table values as another base table.
Update 2:
Given the real query now presented, this is how the values from table values could be incorporated into it:
SELECT x.fantasycoach_id, SUM(round_points) FROM
(
SELECT DISTINCT
fc.id AS fantasycoach_id,
ffv.formation_id AS formation_id,
fpc.round_sequence AS round_sequence,
round_points,
fpc.fantasyplayer_id
FROM fantasyworld_FantasyCoach AS fc
-- one row for each combination of coach and value:
CROSS JOIN values
LEFT JOIN fantasyworld_fantasyformation AS ff
ON ff.id = (
SELECT MAX(fantasyworld_fantasyformationvalidity.formation_id)
FROM fantasyworld_fantasyformationvalidity
LEFT JOIN realworld_round AS _rr
ON _rr.id = round_id
LEFT JOIN fantasyworld_fantasyformation AS _ff
ON _ff.id = formation_id
WHERE is_valid = TRUE
AND _ff.coach_id = fc.id
-- use the value obtained from values:
AND _rr.sequence <= values.value
)
LEFT JOIN fantasyworld_FantasyFormationPlayer AS ffp
ON ffp.formation_id = ff.id
LEFT JOIN dbcache_fantasyplayercache AS fpc
ON ffp.player_id = fpc.fantasyplayer_id
-- use the value obtained from values again:
AND fpc.round_sequence = values.value
LEFT JOIN fantasyworld_fantasyformationvalidity AS ffv
ON ffv.formation_id = ff.id
) x
GROUP BY fantasycoach_id
Note in particular the CROSS JOIN which forms the cross product of two tables; this is the same thing as an INNER JOIN without any join predicate, and it can be written that way if desired.
The overall query could be at least a bit simplified, but I do not do so because it is a working example rather than an actual production query, so it is unclear what other changes would translate to the actual application.
In the example I create two tables. See how outer table have an alias you use in the inner select?
SQL Fiddle Demo
SELECT T.[value], (SELECT [property] FROM Table2 P WHERE P.[value] = T.[value])
FROM Table1 T
This is a better way for performance
SELECT T.[value], P.[property]
FROM Table1 T
INNER JOIN Table2 p
on P.[value] = T.[value];
Table 2 can be a QUERY instead of a real table
Third Option
Using a cte to calculate your values and then join back to the main table. This way you have the subquery logic separated from your final query.
WITH cte AS (
SELECT
T.[value],
T.[value] * T.[value] as property
FROM Table1 T
)
SELECT T.[value], C.[property]
FROM Table1 T
INNER JOIN cte C
on T.[value] = C.[value];
It might be helpful to extract the computation to a function that is called in the SELECT clause and is executed for each row of the result set
Here's the documentation for CREATE FUNCTION for SQL Server. It's probably similar to whatever database system you're using, and if not you can easily Google for it.
Here's an example of creating a function and using it in a query:
CREATE FUNCTION DoComputation(#parameter1 int)
RETURNS int
AS
BEGIN
-- Do some calculations here and return the function result.
-- This example returns the value of #parameter1 squared.
-- You can add additional parameters to the function definition if needed
DECLARE #Result int
SET #Result = #parameter1 * #parameter1
RETURN #Result
END
Here is an example of using the example function above in a query.
SELECT v.value, DoComputation(v.value) as ComputedValue
FROM [Values] v
ORDER BY value

Performance tuning of row-based subqueries: LEFT OUTER JOIN and OUTER APPLY, alternatives?

The performance of a certain query (on a Dynamics CRM 2011 database) was abysmal. Since it is a normalized datamodel, but a flattened view on this data (an SSRS report) is required, I did a lot (12) of LEFT OUTER JOINs with a SELECT TOP (1) subquery, e.g.:
LEFT JOIN Filterednew_rates FRates ON FRates.new_ratesid =
(SELECT TOP (1)
FRR.new_ratesid
FROM Filterednew_rates FRR
WHERE
FRR.new_contractid = FContract.contractid
AND FRR.statuscode <> 803270000 -- NOT Obsolete
ORDER BY FRR.new_startdate DESC
)
This worked for a small number of result rows (like 10 seconds for 3 rows), but I've had it run for 45 minutes on about 100 expected result rows (the amount of source data is the same, just different WHERE clause). So I started looking for ways to "force" SQL Server to run the subqueries per row (since logically to me, that would scale linearly).
Then I read The power of T-SQL's APPLY operator and managed to change the above to
OUTER APPLY (
SELECT TOP (1)
FRR.*
FROM Filterednew_rates FRR
WHERE
FRR.new_contractid = FContract.contractid
AND FRR.statuscode <> 803270000 -- NOT Obsolete
ORDER BY FRR.new_startdate DESC
) AS FRates
Which made the execution time scale about linearly with the number of result records (about 3:30 minutes for 100 rows, still about 6 seconds for 3 rows). Somehow this made SQL Server decide to change the query execution plan for the better!
Is there any other way in SQL to "flatten" a normalized datamodel without resorting to Integration/Analysis Services?
EDIT:
Thanks for the input #Aaron and #BAReese. I'll try to apply PIVOT/UNPIVOT and the Windowing Functions and report back on query performance differences.
And by popular request, a larger part of the query. I've tried to "anonymize" the query a bit, so the actual query properties are more descriptive.
OUTER APPLY (
SELECT TOP (1)
FCO.*
FROM Filterednew_contractoption FCO
WHERE
FCO.new_contractid = FContract.contractid
AND FCO.new_included = 1 -- Is Included
AND FCO.new_optionidname = 'SomeOption1'
) AS FOptionSomeOption1
OUTER APPLY (
SELECT TOP (1)
FCO.*
FROM Filterednew_contractoption FCO
WHERE
FCO.new_contractid = FContract.contractid
AND FCO.new_included = 1 -- Is Included
AND FCO.new_optionidname = 'SomeOption2'
) AS FOptionSomeOption2
OUTER APPLY (
SELECT TOP (1)
FCD.*
FROM FilteredContractDetail FCD
JOIN FilteredProduct FProd ON FCD.productid = FProd.productid
WHERE
FContract.contractid = FCD.contractid
AND FCD.new_included = 1 -- Is Included
AND FProd.productnumber IN ('COLDEL1', 'COLDEL2', 'COLDEL3', 'COLDEL4')
) AS FColDelContractDetail
LEFT JOIN FilteredProduct FColDelProduct ON FColDelContractDetail.productid = FColDelProduct.productid
OUTER APPLY (
SELECT TOP (1)
FCO.*
FROM Filterednew_contractoption FCO
JOIN Filterednew_contractdetail_new_contractoptions FCD_CO ON FCO.new_contractoptionid = FCD_CO.new_contractoptionid
WHERE
FCD_CO.contractdetailid = FColDelContractDetail.contractdetailid
AND FCO.new_included = 1 -- Is Included
AND FCO.new_optionidname LIKE 'Input1'
) AS FColDelInput1Option
OUTER APPLY (
SELECT TOP (1)
FCO.*
FROM Filterednew_contractoption FCO
JOIN Filterednew_contractdetail_new_contractoptions FCD_CO ON FCO.new_contractoptionid = FCD_CO.new_contractoptionid
WHERE
FCD_CO.contractdetailid = FColDelContractDetail.contractdetailid
AND FCO.new_included = 1 -- Is Included
AND FCO.new_optionidname LIKE 'Input2'
) AS FColDelInput2Option
OUTER APPLY (
SELECT TOP (1)
FCO.*
FROM Filterednew_contractoption FCO
JOIN Filterednew_contractdetail_new_contractoptions FCD_CO ON FCO.new_contractoptionid = FCD_CO.new_contractoptionid
WHERE
FCD_CO.contractdetailid = FColDelContractDetail.contractdetailid
AND FCO.new_included = 1 -- Is Included
AND FCO.new_optionidname LIKE 'Input3'
) AS FColDelInput3Option
OUTER APPLY (
SELECT TOP (1)
FCP.*
FROM Filterednew_price FCP
WHERE FCP.new_contractid = FContract.contractid
AND FCP.statuscode <> 803270000 -- NOT Obsolete
ORDER BY FCP.new_validfrom DESC
) AS FPrice
OUTER APPLY (
SELECT TOP (1)
FCFR.*
FROM Filterednew_contractforecastresult FCFR
WHERE FCFR.new_contractid = FContract.contractid
ORDER BY FCFR.createdon DESC
) AS FForecastResult
Since you're using SQL Server, this would be an excellent opportunity to use windowing functions to improve efficiency.
something like this might help it run quicker:
LEFT JOIN
(
SELECT FRR.new_contractid, ROW_NUMBER() over(partition by FRR.new_contractid
order by FRR.new_startdate DESC) as Last_ID
FROM Filterednew_rates as FRR
WHERE FRR.statuscode <> 803270000 -- NOT Obsolete
) AS FRates
ON FRates.new_contractid = FContract.contractid
and FRates.Last_ID = 1
What this should do is allow the derived table to produce a list of all contractids but give a priority list. In theory, it will be easier on the server and you won't be hitting the table more times than necessary. Another thing you can do is add SET STATISTICS IO ON and SET STATISTICS TIME ON to the top of your query (assuming you're testing this in SQL Server Management Studio). If in SSMS, you'll get a log on the [Messages] tab telling what the logical/physical read count of each table is, as well as the amount of time spent querying.