2016-12-06 2 views
2

j'ai les tableaux ci-dessous mis en place:Remplacement d'une requête en boucle

CREATE TABLE app_detail 
(
CustID1 int, 
CustID2 int, 
AppDate datetime 
) 

CREATE TABLE inv_detail 
(
CustID1 int, 
CustID2 int, 
PostDate datetime, 
ClearDate datetime, 
Amt float 
) 

INSERT INTO app_detail 
VALUES(583,246,'2013-04-30 00:00:00.000') 
INSERT INTO app_detail 
VALUES(583,246,'2015-06-17 00:00:00.000') 

INSERT INTO inv_detail 
VALUES(583,246,'2013-04-05 00:00:00.000',NULL,17.56) 
INSERT INTO inv_detail 
VALUES(583,246,'2013-04-05 00:00:00.000','2013-12-31 00:00:00.000',667.97) 
INSERT INTO inv_detail 
VALUES(583,246,'2013-04-05 00:00:00.000','2014-10-05 00:00:00.000',3.96) 
INSERT INTO inv_detail 
VALUES(583,246,'2013-04-05 00:00:00.000',NULL,48.40) 
INSERT INTO inv_detail 
VALUES(583,246,'2013-01-08 00:00:00.000','2013-12-31 00:00:00.000',332.03) 
INSERT INTO inv_detail 
VALUES(583,246,'2013-01-08 00:00:00.000','2013-12-31 00:00:00.000',63.10) 


INSERT INTO inv_detail 
VALUES(583,246,'2013-07-09 00:00:00.000',NULL,1062.29) 
INSERT INTO inv_detail 
VALUES(583,246,'2013-04-05 00:00:00.000',NULL,17.56) 
INSERT INTO inv_detail 
VALUES(583,246,'2013-04-05 00:00:00.000',NULL,48.40) 

select * from app_detail 
select * from inv_detail 

je voudrais obtenir la sortie suivante:

SELECT 
'583' AS CustID1 
,'246' AS CustID2 
,'2013-04-30 00:00:00.000' AS AppDate 
,'1133.02' AS TotalAmount 

UNION 
SELECT 
'583' 
,'246' 
,'2015-06-17 00:00:00.000' 
,'1128.25' 

CustID1 CustID2 AppDate TotalAmount 
583  246 2013-04-30 1133.02 
583  246 2015-06-17 1128.25 

Les premières tables contient des données sur les clients qui font une application sur un date particulière. La deuxième table contient les détails de facturation pour ces clients, quand la facture a été envoyée et quand elle a été payée (compensée). Je veux connaître le montant de la facturation que le client avait en circulation à la date de la demande. Si la facture a été payée, il y aura une date dans la colonne ClearedDate. Si la facture n'a jamais été payée, elle sera NULL.

La seule façon dont j'ai pensé à le faire est via une boucle où je viens de passer un AppDate à la fois à la clause WHERE. Mais j'espérais pouvoir annuler cela.

Toute aide serait appréciée.

+2

Comment calculez-vous le montant total? Je ne peux pas suivre. Pardon. –

Répondre

2

Emprunté à la réponse de Blorgbeard pour montrer comment cela pouvait se faire sans la sous-requête.

DECLARE @app_detail TABLE (CustID1 int,CustID2 int,AppDate datetime) 
DECLARE @inv_detail TABLE(CustID1 int,CustID2 int,PostDate datetime,ClearDate datetime,Amt float) 

INSERT INTO @app_detail VALUES(583,246,'2013-04-30 00:00:00.000') 
INSERT INTO @app_detail VALUES(583,246,'2015-06-17 00:00:00.000') 

INSERT INTO @inv_detail VALUES(583,246,'2013-04-05 00:00:00.000',NULL,17.56) 
INSERT INTO @inv_detail VALUES(583,246,'2013-04-05 00:00:00.000','2013-12-31 00:00:00.000',667.97) 
INSERT INTO @inv_detail VALUES(583,246,'2013-04-05 00:00:00.000','2014-10-05 00:00:00.000',3.96) 
INSERT INTO @inv_detail VALUES(583,246,'2013-04-05 00:00:00.000',NULL,48.40) 
INSERT INTO @inv_detail VALUES(583,246,'2013-01-08 00:00:00.000','2013-12-31 00:00:00.000',332.03) 
INSERT INTO @inv_detail VALUES(583,246,'2013-01-08 00:00:00.000','2013-12-31 00:00:00.000',63.10) 
INSERT INTO @inv_detail VALUES(583,246,'2013-07-09 00:00:00.000',NULL,1062.29) 
INSERT INTO @inv_detail VALUES(583,246,'2013-04-05 00:00:00.000',NULL,17.56) 
INSERT INTO @inv_detail VALUES(583,246,'2013-04-05 00:00:00.000',NULL,48.40) 

select a.*, sum(Amt) AS [Total] 
from @app_detail a 
LEFT JOIN @inv_detail i ON i.CustID1=a.CustID1 
    AND i.CustID2=a.CustID2 
    AND i.PostDate <= a.AppDate 
    AND (i.ClearDate is null or i.ClearDate > a.AppDate) 
GROUP BY a.CustID1,a.CustID2,a.AppDate 
+0

Cela semble bien, et c'est plus simple - mais OP peut souhaiter tester ses performances par rapport à ma réponse sur ses données réelles. Mon test sur les données de test minimales indiquait que c'était en fait plus lent - il faisait une jointure en boucle imbriquée plus un tri, par rapport à 'outer apply' ne faisant que la jointure en boucle imbriquée. – Blorgbeard

+0

@Blorgbeard Si je devais le réécrire pour moi-même, j'utiliserais un syndicat et me débarrasserais de l'OR. J'ai toujours trouvé cela plus efficace. Après l'analyse comparative et seulement si la mise en œuvre de l'OR n'était pas assez rapide bien sûr :) – UnhandledExcepSean

2

Vous pouvez le faire en utilisant outer apply au lieu d'une boucle manuelle:

select a.*, due.Total 
from app_detail a 
outer apply (
    select sum(Amt) [Total] 
    from inv_detail i 
    where 
     i.CustID1=a.CustID1 and i.CustID2=a.CustID2 and 
     i.PostDate <= a.AppDate and (i.ClearDate is null or i.ClearDate > a.AppDate) 
) due 

Il renvoie les résultats corrects pour moi, après avoir enlevé deux lignes en double de vos données de test (17.56 et 48.40 les deux avaient deux entrées) .

Cette méthode n'est pas particulièrement efficace. Elle entraînera un balayage de table (ou éventuellement un index, si vous avez un index approprié) de inv_detail pour chaque ligne de app_detail. Cependant, je ne crois pas qu'il existe un moyen de contourner cela dans ce cas - ce n'est pas un simple agrégat, car une ligne de inv_detail peut être impliquée dans le calcul pour de nombreuses lignes de app_detail.