J'ai besoin de votre aide, j'ai besoin de la guanade pour améliorer la performance de la vue donnée suivante.
J'ai une vue écrive avec le code ci-dessous:
with timeframes as
(
select p.SEARCH_NUM,
case when p.FROM_DATE is not null then p.FROM_DATE
when p.FROM_DATE is null and P.SEARCH_DAYS is not null and p.TO_DATE is not null then DATEADD(day,p.SEARCH_DAYS*-1,p.TO_DATE)
when p.FROM_DATE is null and P.SEARCH_DAYS is not null and p.TO_DATE is null then DATEADD(day,p.SEARCH_DAYS*-1,GetDate())
when p.FROM_DATE is null and P.SEARCH_DAYS is null and p.TO_DATE is not null and p.DURATION = 'Yearly' then DATEADD(year,-1,p.TO_DATE)
when p.FROM_DATE is null and P.SEARCH_DAYS is null and p.TO_DATE is null and p.DURATION = 'Yearly' then DATEADD(year,-1,GetDate())
when p.FROM_DATE is null and P.SEARCH_DAYS is null and p.TO_DATE is not null and p.DURATION = 'Monthly' then DATEADD(month,-1,p.TO_DATE)
when p.FROM_DATE is null and P.SEARCH_DAYS is null and p.TO_DATE is null and p.DURATION = 'Monthly' then DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-1, 0)
when p.FROM_DATE is null and P.SEARCH_DAYS is null and p.TO_DATE is not null and p.DURATION = 'Weekly' then DATEADD(day,-7,p.TO_DATE)
when p.FROM_DATE is null and P.SEARCH_DAYS is null and p.TO_DATE is null and p.DURATION = 'Weekly' then DATEADD(day,-7,GetDate())
else DATEADD(month,-1,GetDate())
end as FROM_DATE
,case when p.TO_DATE is not null then p.TO_DATE
when p.TO_DATE is null and p.DURATION = 'Monthly' then DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1)
else GetDate()
end as TO_DATE
from dbo.parmeters_table as p
)
,
transactions as
(
select tm.SEARCH_NUM, tr.id from dbo.ixf_transaction tr
inner join timeframes tm on tr.transaction_date between tm.FROM_DATE and tm.TO_DATE
)
,
searchResults AS
(
select DISTINCT
t.SEARCH_NUM
,trx.id as 'trx_id_FK'
,trx.institution_FK
,trx.branch_FK
,branch.code as 'branch_number'
,trx.account_FK
,trx.transaction_date as 'trx_date'
,case when trx.type_of_transaction = 'I' or trx.type_of_transaction = 'B'
then trx.base_currency_amount else 0 end as 'cash_in'
,case when trx.type_of_transaction = 'O' or trx.type_of_transaction = 'B'
then trx.base_currency_amount else 0 end as 'cash_out'
,case when trx.type_of_transaction = 'B'
then trx.base_currency_amount else 0 end as 'curr_exchange'
,trx.base_currency_amount
,trx.type_of_transaction
,trx.teller_id
,trx.unique_trans_id
,trx.serial_number
,trx.foreign_amount
,trx.country_of_currency_FK
,trx.flex_1
,trx.flex_2
,trx.customer_FK
,ttr.code as 'trx_code'
,ttr.description as 'trx_description'
,ttr.irs_transaction_id
,cust.full_name
,str(cust.web_reference_id) as 'web_reference_id'
,conben.customer_cif
,conben.id_number
,conben.id_type
,conben.other_description as 'id_type_other_description'
,conben.customer_tin
,conben.cust_type_fk as 'TIN_Type'
,ctrtx.is_teller
from ixf_transaction trx
inner join transactions t on trx.id = t.id
inner join type_ref ttr on ttr.id=trx.transaction_type_FK
inner join unit branch on branch.id=trx.branch_FK and branch.object_type='Branch'
left join cust on cust.id = trx.customer_FK
left join cashtx_cus conben on conben.transaction_id = trx.unique_trans_id and conben.customer_FK = trx.customer_FK
left join trans ctrtx on ctrtx.cash_transaction_fk = trx.id
)
select trx_id_fk
,case when account_FK is not null then (select count(1) from searchresults a where a.account_fk = searchresults.account_fk and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when customer_cif is not null then (select count(1) from searchresults a where a.customer_cif = searchresults.customer_cif and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when customer_tin is not null then (select count(1) from searchresults a where a.customer_tin = searchresults.customer_tin and a.TIN_Type = searchresults.TIN_Type and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when ID_NUMBER is not null then (select count(1) from searchresults a where a.ID_NUMBER = searchresults.ID_NUMBER and a.SEARCH_NUM = searchresults.SEARCH_NUM)
else (select count(1) from searchresults a where a.SEARCH_NUM = searchresults.SEARCH_NUM)
end as 'trx_per_period'
,case when account_FK is not null then (select count(1) from searchresults a where a.account_FK = searchresults.account_FK and a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when customer_cif is not null then (select count(1) from searchresults a where a.customer_cif = searchresults.customer_cif and a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when customer_tin is not null then (select count(1) from searchresults a where a.customer_tin = searchresults.customer_tin and a.TIN_Type = searchresults.TIN_Type and a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when ID_NUMBER is not null then (select count(1) from searchresults a where a.ID_NUMBER = searchresults.ID_NUMBER and a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
else (select count(1) from searchresults a where a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
end as 'trx_per_day'
,case when account_FK is not null then (select sum(cash_in) from searchresults a where a.account_FK = searchresults.account_FK and a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when customer_cif is not null then (select sum(cash_in) from searchresults a where a.customer_cif = searchresults.customer_cif and a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when customer_tin is not null then (select sum(cash_in) from searchresults a where a.customer_tin = searchresults.customer_tin and a.TIN_Type = searchresults.TIN_Type and a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when ID_NUMBER is not null then (select sum(cash_in) from searchresults a where a.ID_NUMBER = searchresults.ID_NUMBER and a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
else (select sum(cash_in) from searchresults a where a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
end as 'total_per_day_cash_in'
,case when account_FK is not null then (select sum(cash_out) from searchresults a where a.account_FK = searchresults.account_FK and a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when customer_cif is not null then (select sum(cash_out) from searchresults a where a.customer_cif = searchresults.customer_cif and a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when customer_tin is not null then (select sum(cash_out) from searchresults a where a.customer_tin = searchresults.customer_tin and a.TIN_Type = searchresults.TIN_Type and a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when ID_NUMBER is not null then (select sum(cash_out) from searchresults a where a.ID_NUMBER = searchresults.ID_NUMBER and a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
else (select sum(cash_out) from searchresults a where a.trx_date = searchresults.trx_date and a.SEARCH_NUM = searchresults.SEARCH_NUM)
end as 'total_per_day_cash_out'
,case when account_FK is not null then (select sum(cash_in) from searchresults a where a.account_FK = searchresults.account_FK and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when customer_cif is not null then (select sum(cash_in) from searchresults a where a.customer_cif = searchresults.customer_cif and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when customer_tin is not null then (select sum(cash_in) from searchresults a where a.customer_tin = searchresults.customer_tin and a.TIN_Type = searchresults.TIN_Type and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when ID_NUMBER is not null then (select sum(cash_in) from searchresults a where a.ID_NUMBER = searchresults.ID_NUMBER and a.SEARCH_NUM = searchresults.SEARCH_NUM)
else (select sum(cash_in) from searchresults a where a.SEARCH_NUM = searchresults.SEARCH_NUM)
end as 'total_per_period_cash_in'
,case when account_FK is not null then (select sum(cash_out) from searchresults a where a.account_FK = searchresults.account_FK and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when customer_cif is not null then (select sum(cash_out) from searchresults a where a.customer_cif = searchresults.customer_cif and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when customer_tin is not null then (select sum(cash_out) from searchresults a where a.customer_tin = searchresults.customer_tin and a.TIN_Type = searchresults.TIN_Type and a.SEARCH_NUM = searchresults.SEARCH_NUM)
when ID_NUMBER is not null then (select sum(cash_out) from searchresults a where a.ID_NUMBER = searchresults.ID_NUMBER and a.SEARCH_NUM = searchresults.SEARCH_NUM)
else (select sum(cash_out) from searchresults a where a.SEARCH_NUM = searchresults.SEARCH_NUM)
end as 'total_per_period_cash_out'
from SearchResults
La partie lourde est le résultat de la recherche CTE qui s'appelle encore et encore, et le plan d'exécution a l'air ci-dessous:
Existe-t-il un moyen d'éviter cela en obtenant les mêmes résultatsset et d'éviter ce forte plan d'exécution?
Je pense que vous pouvez écrire du code sous une hypothèse défectueuse: les résultats de la CTE sont persistés
Ils ne sont pas et chaque fois que vous les référez, la syntaxe est ré-exécutée.
Voici un exemple rapide:
CREATE TABLE #dummy
(
id INT
);
INSERT #dummy ( id )
VALUES ( 1 );
WITH yourmom
AS ( SELECT d.id
FROM #dummy AS d )
SELECT ym.id
FROM yourmom AS ym
JOIN yourmom AS ym2
ON ym2.id = ym.id
JOIN yourmom AS ym3
ON ym3.id = ym.id;
Si vous regardez le Plan de requête , il existe trois analyses de la table de base - une fois pour la version initiale FROM
et une fois pour chaque JOIN
. C'est trois au total.
Cela provoque des problèmes dans quelques endroits:
transactions
CTEtransactions
in searchResults
COUNT
sous-requêtes dans votre choix final de searchresults
Cela conduit à votre deuxième problème: ces deux CASE
expressions que vous utilisez pour déterminer vos dates FROM
et TO
sont totalement non sargable .
Vous avez deux options:
Collez le résultat de votre premier CTE dans un #temp table
Ajoutez des colonnes calculées à votre table de base
J'en choisirais probablement les colonnes calculées dans ce cas
ALTER TABLE dbo.parmeters_table
ADD FROM_DATE_SEARCHED AS CASE WHEN FROM_DATE IS NOT NULL THEN FROM_DATE
WHEN FROM_DATE IS NULL
AND SEARCH_DAYS IS NOT NULL
AND TO_DATE IS NOT NULL THEN DATEADD(DAY, SEARCH_DAYS * -1, TO_DATE)
WHEN FROM_DATE IS NULL
AND SEARCH_DAYS IS NOT NULL
AND TO_DATE IS NULL THEN DATEADD(DAY, SEARCH_DAYS * -1, GETDATE())
WHEN FROM_DATE IS NULL
AND SEARCH_DAYS IS NULL
AND TO_DATE IS NOT NULL
AND DURATION = 'Yearly' THEN DATEADD(YEAR, -1, TO_DATE)
WHEN FROM_DATE IS NULL
AND SEARCH_DAYS IS NULL
AND TO_DATE IS NULL
AND DURATION = 'Yearly' THEN DATEADD(YEAR, -1, GETDATE())
WHEN FROM_DATE IS NULL
AND SEARCH_DAYS IS NULL
AND TO_DATE IS NOT NULL
AND DURATION = 'Monthly' THEN DATEADD(MONTH, -1, TO_DATE)
WHEN FROM_DATE IS NULL
AND SEARCH_DAYS IS NULL
AND TO_DATE IS NULL
AND DURATION = 'Monthly' THEN DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - 1, 0)
WHEN FROM_DATE IS NULL
AND SEARCH_DAYS IS NULL
AND TO_DATE IS NOT NULL
AND DURATION = 'Weekly' THEN DATEADD(DAY, -7, TO_DATE)
WHEN FROM_DATE IS NULL
AND SEARCH_DAYS IS NULL
AND TO_DATE IS NULL
AND DURATION = 'Weekly' THEN DATEADD(DAY, -7, GETDATE())
ELSE DATEADD(MONTH, -1, GETDATE())
END,
TO_DATE_SEARCHED AS CASE WHEN TO_DATE IS NOT NULL THEN TO_DATE
WHEN TO_DATE IS NULL
AND DURATION = 'Monthly' THEN DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE()) - 1, -1)
ELSE GETDATE()
END;
Vous pouvez indexer ces colonnes pour faire votre jointure à dbo.ixf_transaction
plus efficace et évitez de devoir pré-traiter toutes les données, puis y participer.
Et je me pencherais vers une table Temp pour persister les résultats de searchResults
. Cela empêche le besoin de réécruder pour chaque expression CASE
.
J'espère que cela t'aides!
Existe-t-il un moyen d'éviter cela en obtenant les mêmes résultatsset et d'éviter ce forte plan d'exécution?
Essayez de persister le résultat de CTE SearchResult à une table Temp et utilisez la table TEM dans la requête principale à la place.
En bref, on dirait que vous essayez de faire trop d'une chose en une fois: o)
Ce dernier grand SELECT
en bas avec tous les expressions parallèles CASE
font partie du problème. Vous devriez tout faire en dessous de cette vue, puis casser cette grande requête à la fin dans l'une quelconque de l'une quelconque JOIN
contre elle (avec ces conditions de la clause ON
) ou éventuellement cinq requêtes distinctes pour correspondre chacune de ces conditions. De toute façon, vous voulez vraiment vraiment vous débarrasser de ces sous-sélectionnements dans les cas (surtout car ils semblent faire une SUM
de la même colonne de chaque branche du CASE
Sauf si j'ai raté quelque chose ).
EDIT: OK, je parlais trop tôt, vous semblez avoir des conditions différentes dans certaines des sous-sélectionneuses, mais même pour que vous puissiez réduire cela à deux JOINS
, ou encore peut-être seulement deux requêtes distinctes, une pour les données qui nécessitent le a.trx_date = searchresults.trx_date
condition et une pour les données qui ne le font pas. Vous comprenez probablement les données mieux à ce stade, alors je ne vais pas continuer à échapper à la profondeur de ma profondeur.
Je transformerais probablement quelques succursales CASE
dans votre timeframes
CTE dans COALESCE
mais c'est juste une question de style et/ou de goût.