Hello All,
Is their any way I can avoid a Table Scan by modifying the follow WHERE clause
WHERE tbl_mis_Dashboard.loan_active_flg = 1
AND [tbl_mis_Dashboard].[loan_create_dt]
between DATEADD(dd, DATEDIFF(dd,0,dateadd(mm,-13,getdate())), 0)
And getdate()
The field loan_create_dt is indexed on the table tbl_mis_Dashboard. The query returns approximately 256K records which is appropriate and necessary.
Thanks all,
Orlanzo

How To Avoid Table Scan
Greetings
I appreciate your response. The entire query is listed below. The column loan_active.flg is indexed. Neither it or the index for loan_create_dt is clustered. As you will see, I'm selecting quite a few fields. I've read including too many in covering index isn't always the most efficient. Would you agree
Regards,
Orlanzo
SELECT Year([loan_D1_combined_fund_dt]) AS [Year], Month([loan_D1_combined_fund_dt]) AS [Month], Count(dbo_tbl_mis_DashBoard.loan_id) AS [Total Loans], Sum(dbo_tbl_mis_DashBoard.loan_loan_amt) AS [Total Loan Amount], Avg(dbo_tbl_mis_DashBoard.loan_loan_amt) AS [Average Loan Amount], Min(dbo_tbl_mis_DashBoard.loan_loan_amt) AS [Minimum Loan Amount], Max(dbo_tbl_mis_DashBoard.loan_loan_amt) AS [Maximum Loan Amount], Avg(dbo_tbl_mis_DashBoard.loan_note_rate) AS [Ave Loan Rate], Sum(dbo_tbl_mis_Dashboard!loan_note_rate*dbo_tbl_mis_Dashboard!loan_loan_amt) AS [WAC Step1], Sum(IIf(dbo_tbl_mis_Dashboard!loan_doc_type_label="Stated Income",dbo_tbl_mis_Dashboard!loan_loan_amt,0)) AS [$ % Stated], Sum(IIf(dbo_tbl_mis_Dashboard!loan_lien_position=1,IIf(dbo_tbl_mis_Dashboard!product_type_label="FIXED",dbo_tbl_mis_Dashboard!loan_loan_amt,0))) AS [$ % of FIXED Lien 1], Sum(IIf(Left(dbo_tbl_mis_Dashboard!product_label,2)="IO",dbo_tbl_mis_Dashboard!loan_loan_amt,0)) AS [$ % IO], Sum(IIf(dbo_v_mis_Report_Status_Pivot![Problem Performing_dt] Is Not Null,dbo_tbl_mis_Dashboard!loan_loan_amt,IIf(dbo_v_mis_Report_Status_Pivot![Problem Non-Performing_dt] Is Not Null,dbo_tbl_mis_Dashboard!loan_loan_amt,IIf(dbo_v_mis_Report_Status_Pivot![Problem/REO_dt] Is Not Null,dbo_tbl_mis_Dashboard!loan_loan_amt,IIf(dbo_v_mis_Report_Status_Pivot![Problem/D1Portfolio_dt] Is Not Null,dbo_tbl_mis_Dashboard!loan_loan_amt,IIf(dbo_tbl_mis_Dashboard!LExt_rprch_ind<>0,dbo_tbl_mis_Dashboard!loan_loan_amt,0)))))) AS [Problem_Loan_$%], Sum(IIf(dbo_tbl_mis_Dashboard!product_label Like "*40*",dbo_tbl_mis_Dashboard!loan_loan_amt,0)) AS [$ % 40 Yr Step1], Sum(dbo_tbl_mis_Dashboard!loan_CLTV*dbo_tbl_mis_Dashboard!loan_loan_amt) AS [CLTV Step1], Sum(IIf(dbo_v_mis_Report_Status_Pivot!Redirected_dt Is Not Null,1,0)) AS Redirected_dt_Step1, Sum(IIf(dbo_v_mis_Report_Status_Pivot![Pended At Support Office_dt] Is Not Null,1,0)) AS Pended_at_SO_dt_Step1, Sum(IIf(dbo_tbl_mis_Dashboard!lsh_status_label="Paid Off",dbo_tbl_mis_Dashboard!loan_loan_amt,0)) AS [$ % Paid Off], Sum(IIf(dbo_tbl_mis_DashBoard!lsh_current_status_id=27,dbo_tbl_mis_DashBoard!loan_loan_amt,IIf(dbo_tbl_mis_DashBoard!lsh_current_status_id=29,dbo_tbl_mis_DashBoard!loan_loan_amt,IIf(dbo_tbl_mis_DashBoard!lsh_current_status_id=47,dbo_tbl_mis_DashBoard!loan_loan_amt,IIf(dbo_tbl_mis_DashBoard!lsh_current_status_id=48,dbo_tbl_mis_DashBoard!loan_loan_amt,IIf(dbo_tbl_mis_DashBoard!LExt_rprch_ind<>0,dbo_tbl_mis_DashBoard!loan_loan_amt,0)))))) AS [Active Problem Loans $ %]
FROM dbo_tbl_mis_DashBoard LEFT JOIN dbo_v_mis_Report_Status_Pivot ON (dbo_tbl_mis_DashBoard.company_id = dbo_v_mis_Report_Status_Pivot.company_id) AND (dbo_tbl_mis_DashBoard.loan_id = dbo_v_mis_Report_Status_Pivot.loan_id)
WHERE (((dbo_tbl_mis_DashBoard.loan_active_flg)=1) AND ((dbo_tbl_mis_DashBoard.FUND_Date_Flag)="Yes") AND ((dbo_tbl_mis_DashBoard.loan_D1_combined_fund_dt)<Now()))
GROUP BY Year([loan_D1_combined_fund_dt]), Month([loan_D1_combined_fund_dt])
ORDER BY Year([loan_D1_combined_fund_dt]) DESC , Month([loan_D1_combined_fund_dt]) DESC;
jrasmussen
INSERT #xyz
SELECT DateAdd(ms, number * -1, @DT)
FROM master..spt_values
CREATE CLUSTERED INDEX IX_X ON #XYZ (Date1)
SET STATISTICS PROFILE ON
SELECT Date1
FROM #xyz
WHERE Date1 < GetDate()
FROM #xyz
WHERE Date1 < @DT
SET STATISTICS PROFILE OFF
DROP TABLE #xyz
Looter
First, Show Execution Plan shows actual execution rather than estimated. It shows a graphical form of statistics profile. (http://www.microsoft.com/technet/community/chats/trans/sql/chat0503.mspx)
Second, using the statistics profile with a variable consistantly produces lower cost.
This is consistent in real world data and can mean real time in larger tables.
MichaelPolak
It's quite funny really. I get this question all the time. The answer is:
Sometimes table scans are good, sometimes they are bad. It depends on your data, your index's (covering/cluster index's etc...) and what joins you are doing.
To optimise your query, you need to look at the subtree cost inside of Query Analyser (or SQL Server Management Studio for 2005). If adding index's reduces the subtree cost, then they are worth having. If it makes the subtree cost worse, drop them and let the table scan happen.
By looking at your query, I would suggest that you might actually benefit from de-normalization. I.e. combine the data into another (single) table and query that table. This is quite common in data warehousing. You can use DTS or SSIS to do this.
Regards
Paul
Jstn
While it may not avoid a table scan, if your query is in a stored procedure, you could do something like this:
Declare @DT datetime
Set @DT = getDate()
WHERE tbl_mis_Dashboard.loan_active_flg = 1
AND [tbl_mis_Dashboard].[loan_create_dt]
between DATEADD(dd, DATEDIFF(dd,0,dateadd(mm,-13,@DT)), 0)
And @DT
I've seen this costing about a third of the other way. Reason getDate() isn't deterministic, therefore has to compute on every row.
Venky_SG
Thaks so much everyone for your time and input. Everything has been very helpful. And Adam, I'll certainly pick up the books you listed. It lloks like I've going to have a bit of fun in the upcoming months becoming more proficient in performance tuning and optimization. Again, thanks everyone for getting me on the right track.
Regards,
Orlanzo
rDNA
Along similar lines, if tbl_mis_Dashboard.load_active_flg is a bit field, it will have to be CAST to an int for every row in order to compare it to 1.
Try WHERE tbl_mis_Dashboard.load_active_flg = CAST(1, bit)
Lieblick
--
Adam Machanic
Pro SQL Server 2005, available now
http://www..apress.com/book/bookDisplay.html bID=457
--
lucasjam
Actually this is closer to the original problem, a GetDate() with a between. Show execution plan and you will see the cost diff. You can reverse the queries and get the same results, so it isn't benefitting from cache.
DECLARE @DT datetime
SET @DT = getDate()
CREATE TABLE #xyz (Date1 DateTime, Date2 DateTime)
INSERT #xyz
SELECT DateAdd(ms, number * -1, @DT), DateAdd(ms, number, @DT)
FROM master..spt_values
CREATE CLUSTERED INDEX IX_X ON #XYZ (Date1, Date2)
SET STATISTICS PROFILE ON
SELECT Date1, Date2
FROM #xyz
WHERE Date1 BETWEEN Date2 AND GETDATE()
SELECT Date1, Date2
FROM #xyz
WHERE Date1 BETWEEN Date2 AND @DT
SET STATISTICS PROFILE OFF
DROP TABLE #xyz
Ike
Thank you both for your input. It is very much appreciated. The query I provided earlier was not the one in question; however, as you mentioned Paul - denormalizing the data for reporting would appear to be beneficial in that case. Below is the SQL corresponding o the WHERE clause of my original post. In this situation I'm pulling from a single table.
Paul - I'll review the execution plan and do some trial and error exercises with the indexes on the table; including, the creation of a compound index as Adam suggested.
Is their anything of particular importance I should be aware of when reviewing/obtaining an execution plan from within Query Analyzer
Thanks again,
Orlanzo
BTW - I'm using SQL Server 2000.
select tbl_mis_Dashboard.loan_active_flg,
tbl_mis_Dashboard.Month_loan_create_dt AS [Month Create Date],
tbl_mis_Dashboard.Year_loan_create_dt AS [Year Create Date],
tbl_mis_Dashboard.loan_id,
tbl_mis_Dashboard.company_id,
convert(char(10), tbl_mis_Dashboard.loan_create_dt,101) AS [create date],
convert(char(10),tbl_mis_Dashboard.loan_create_dt,101) as [Loan Create Date],
tbl_mis_Dashboard.loan_orig_company_id,
tbl_mis_Dashboard.loan_purpose_cd,
tbl_mis_Dashboard.loan_purpose_label,
tbl_mis_Dashboard.loan_doc_type_cd,
tbl_mis_Dashboard.loan_doc_type_label,
tbl_mis_Dashboard.loan_loan_amt AS loan_loan_amt,
tbl_mis_Dashboard.loan_note_rate AS loan_note_rate,
tbl_mis_Dashboard.loan_lien_position,
tbl_mis_Dashboard.loan_acct_number,
tbl_mis_Dashboard.product_type_label,
tbl_mis_Dashboard.product_label,
tbl_mis_Dashboard.broker_location_id,
tbl_mis_Dashboard.broker_id,
tbl_mis_Dashboard.Branch_Investor_Name,
tbl_mis_Dashboard.[Virtual Branch] AS [Virtual Branch#],
tbl_mis_Dashboard.broker_status_type_cd,
tbl_mis_Dashboard.broker_status_type_label,
tbl_mis_Dashboard.d1_online,
tbl_mis_Dashboard.loan_loan_amt
* tbl_mis_Dashboard.loan_note_rate
AS [WAC loan amt x loan rate],
tbl_mis_Dashboard.loan_loan_amt AS [loan_loan_amt IS Null],
tbl_mis_Dashboard.loan_note_rate AS [loan_note_rate IS NULL],
tbl_mis_Dashboard.employee_name,
tbl_mis_Dashboard.orig_type,
tbl_mis_Dashboard.loan_LTV,
tbl_mis_Dashboard.loan_loan_amt
* tbl_mis_Dashboard.loan_LTV AS [WALTV loan amt x loan LTV],
tbl_mis_Dashboard.property_city,
tbl_mis_Dashboard.property_state_cd,
Left(tbl_mis_Dashboard.property_zip,5) AS property_zip,
isnull(tbl_mis_Dashboard.loan_loan_grade_cd,'IS NULL')
AS loan_loan_grade_cd,
tbl_mis_Dashboard.loan_orig_company_label,
convert(char(10),tbl_mis_Dashboard.rate_sheet_date,101) as [Rate Sheet Date],
convert(char(10),tbl_mis_Dashboard.loan_D1_combined_fund_dt,101) as [Loan D1 Combined Fund Date],
convert(char(10),tbl_mis_Dashboard.loan_D1_combined_fund_dt,101) AS [FUND Date],
tbl_mis_Dashboard.broker_location_name,
tbl_mis_Dashboard.lsh_status_label,
tbl_mis_Dashboard.property_structure_label,
tbl_mis_Dashboard.property_occupancy_label,
tbl_mis_Dashboard.loan_CLTV,
tbl_mis_Dashboard.property_rural_flg,
tbl_mis_Dashboard.first_time_flg,
tbl_mis_Dashboard.[Primary Credit Score]
* tbl_mis_Dashboard.loan_loan_amt AS [FICO Step1],
tbl_mis_Dashboard.[Primary Credit Score],
tbl_mis_Dashboard.FUND_Date_Flag AS [FUND Date Flag],
abs(datediff("d",tbl_mis_Dashboard.loan_D1_combined_fund_dt,
tbl_mis_Dashboard.loan_create_dt)) AS [Days to Fund],
tbl_mis_Dashboard.[PrePay Flag] AS [PrePay Flag],
tbl_mis_Dashboard.account_executive,
tbl_mis_Dashboard.BROKER_LOC_street_city AS broker_street_city,
tbl_mis_Dashboard.BROKER_LOC_street_state_cd AS broker_street_state_cd,
tbl_mis_Dashboard.[Current AE assigned to Broker Loc ID],
abs(Datediff("d",tbl_mis_Dashboard.loan_create_dt,
tbl_mis_Dashboard.rate_sheet_date)) AS LPE_Honor_Period,
tbl_mis_Dashboard.[Sales Manager LL],
tbl_mis_Dashboard.loan_debt_ratio,
tbl_mis_Dashboard.gross_margin_disp,
tbl_mis_Dashboard.BROKER_LOC_create_dt,
tbl_mis_Dashboard.broker_comp
FROM tbl_mis_Dashboard
WHERE tbl_mis_Dashboard.loan_active_flg = 1
AND [tbl_mis_Dashboard].[Month_loan_create_dt]
Between Month(getdate())-13 And Month(getdate())
AND [tbl_mis_Dashboard].[Year_loan_create_dt]=Year(getDate())
who_am_I
--
Adam Machanic
Pro SQL Server 2005, available now
http://www..apress.com/book/bookDisplay.html bID=457
--
k m
--
Adam Machanic
Pro SQL Server 2005, available now
http://www..apress.com/book/bookDisplay.html bID=457
--
MarkH001
SELECT number
FROM master..spt_values
FROM #xyz
WHERE x = DATEPART(ms, GETDATE())
--
Adam Machanic
Pro SQL Server 2005, available now
http://www..apress.com/book/bookDisplay.html bID=457
--
Alex Bell
SET @DT = getDate()
SELECT DateAdd(ms, a.number * -1, @DT), DateAdd(ms, b.number, @DT)
FROM master..spt_values a, master..spt_values b
RAISERROR('QUERY 1', 10, 1) WITH NOWAIT
RAISERROR('---------------------------------------------------------', 10, 1) WITH NOWAIT
SET STATISTICS TIME ON
FROM #xyz
WHERE Date1 BETWEEN Date2 AND @DT
SET STATISTICS IO OFF
RAISERROR('---------------------------------------------------------', 10, 1) WITH NOWAIT
SET STATISTICS TIME ON
FROM #xyz
WHERE Date1 BETWEEN Date2 AND GETDATE()
SET STATISTICS IO OFF
--
Adam Machanic
Pro SQL Server 2005, available now
http://www..apress.com/book/bookDisplay.html bID=457
--