2016-07-26 1 views
3

Je souhaite effectuer un calcul via la méthode LIFO (dernier entré, premier sorti) avec TSQL.Calculer les résultats avec la méthode LIFO via TSQL

L'utilisation de la méthode LIFO vous obligera à calculer le bénéfice/la perte en vendant les dernières transactions.

Exemple comment cela fonctionne:

  1. affaire est conclue le 1er mars, nous achetons 10 actions pour 5 dollars chaque
  2. affaire est conclue le 2 mars, nous achetons 15 stocks pour 6 dollars chaque
  3. deal est conclu le 3 mars, nous achetons 5 actions pour 4 dollars chaque
  4. affaire est conclue le 4 mars, nous avons 17 SELL stocks pour 7 dollars chaque

Par la 4ème transaction nous avons maintenant vendu 5 actions du 3 mars pour 4 dollars chacune et 12 actions du 2 mars 6 dollars chacune.

Donc maintenant nous avons quitté ce qui suit: 10 stocks pour 5 dollars chacun de transaction le 1 mars 3 stocks de 6 dollars chacun de la transaction le 2 mars (17-5-15 = -3).

13 stocks ayant quitté avec un prix moyen de (10 * 5 + 3 * 6)/13 = 5,23076923

Voici les scripts de génération de données de test:

use TestTask 
go 
IF OBJECT_ID('testtable','U')IS NOT NULL 
     DROP TABLE testtable 
go 
create table testtable 
(
stockid int not null, 
dealid int identity (1,1) not null, 
dealtype char(1) not null, 
stockdate datetime not null, 
stockamount int not null, 
priceperstock int not null 
) 
insert into testtable(stockid,dealtype,stockdate,stockamount,priceperstock) 
VALUES 
    (111,'B','01.03.2016',10,5), 
    (111,'B','02.03.2016',15,6), 
    (111,'B','03.03.2016',5,4), 
    (111,'S','04.03.2016',17,7) 

Je voudrais calculer la position financière et beaucoup d'autres paramètres qui me requerraient pour savoir combien de stocks avec le prix approprié est parti. Je suis venu à cette jusqu'à présent:

select 
stockid, 
dealid, 
dealtype, 
stockdate, 
priceperstock, 
case dealtype 
    when 'B' then stockamount 
    when 'S' then -stockamount 
end as stockamount, 
sum(
     case dealtype 
      when 'B' then stockamount 
      when 'S' then -stockamount 
     end 
    ) over (partition by 
     stockid order by dealid ROWS UNBOUNDED PRECEDING) 
     as poistion 
from testtable 

Sortie:

stockid dealid dealtype  stockdate   priceperstock stockamount poistion 
    111  1  B  2016-01-03 00:00:00.000  5    10    10 
    111  2  B  2016-02-03 00:00:00.000  6    15    25 
    111  3  B  2016-03-03 00:00:00.000  4    5    30 
    111  4  S  2016-04-03 00:00:00.000  7   -17    13 

sortie souhaitée:

stockid dealid dealtype  stockdate   priceperstock stockamount poistion stocksleft 
    111  1  B  2016-01-03 00:00:00.000  5    10    10  10 
    111  2  B  2016-02-03 00:00:00.000  6    15    25  3 
    111  3  B  2016-03-03 00:00:00.000  4    5    30  0 
    111  4  S  2016-04-03 00:00:00.000  7   -17    13  0 

Quelle est la meilleure façon d'aller?

Répondre

1

Je pense que vous serez probablement désireux de entrelacer vos achète et revend donc alors que je pense que l'autre réponse a été un excellent point de départ, il ne sera pas tout à fait gérer le scénario entier.

Fondamentalement, je pense que vous devrez gérer cela avec une sorte de mécanisme itératif. J'ai essayé de le faire avec récursivité mais malheureusement les fonctions analytiques ne fonctionnent pas correctement avec cette méthode. Donc je suis tombé à une table de temp et tout en boucle.

create table #R (
    lvl int not null, stockId int not null, dealId int not null, 
    stockDate datetime not null, stockAmount int not null, pricePerStock int not null, 
    stockRemaining int not null, amountDeducted int not null 
); 

insert into #R (
    lvl, stockId, dealId, stockDate, stockAmount, 
    pricePerStock, stockRemaining, amountDeducted 
) 
select 0, stockId, dealId, stockDate, stockAmount, pricePerStock, stockAmount, 0 
from <T> where dealtype = 'B' /* <--- your table is <T> */ 

declare @lvl int = 0; 
declare @rowCount int = 1; 
while @rowCount > 0 
begin 
    set @lvl = @lvl + 1; 
    with sells as (
     select stockId, dealId as saleId, 
      row_number() over (order by dealId) as sellNum, stockAmount as sellAmount 
     from <T> where dealType = 'S' 
    ) 
    update #R 
    set stockRemaining = (
     select stockRemaining 
     from (
      select dealId, 
       case 
        when r.stockRemaining + s.sellAmount 
          < sum(stockRemaining) over (order by dealId desc) 
         then r.stockRemaining 
        when sum(stockRemaining) over (order by dealId desc) 
          < s.sellAmount 
         then 0 
        else sum(stockRemaining) over (order by dealId desc) 
          - s.sellAmount 
       end as stockremaining 
      from sells s inner join #R r 
       on r.stockId = s.stockId and r.dealId < s.saleId 
      where s.stockId = #R.stockId and s.sellNum = @lvl 
     ) data 
     where dealId = #R.dealId 
    ) 
    where dealId < (select saleId from sells where sellNum = @lvl); 
    set @rowCount = @@rowCount; 
end 

Je l'ai réduit pour la publication. Voir en action ici avec un peu plus de sortie pour mieux suivre la logique: http://rextester.com/WPLKLJ95730

2

Puisque votre exemple est assez étroit, il sera difficile de mettre en place une solution pare-balles. Mais cela devrait vous aider à démarrer sur la bonne voie, ou au moins sur une piste. Il utilise une sorte de total inverse, puis soustrait du montant de stock. Utilisation de votre ensemble de données avec un petit changement:

create table #testtable 
(
stockid int not null, 
dealid int identity (1,1) not null, 
dealtype char(1) not null, 
stockdate datetime not null, 
stockamount int not null, 
priceperstock int not null 
) 
insert into #testtable(stockid,dealtype,stockdate,stockamount,priceperstock) 
    VALUES 
     (111,'B','01.03.2016',10,5), 
     (111,'B','02.03.2016',15,6), 
     (111,'B','03.03.2016',5,4), 
     (111,'S','04.03.2016',-17,7) --signed int 

----Add this to see another level 
--insert into #testtable(stockid,dealtype,stockdate,stockamount,priceperstock) 
-- VALUES 
--  (111,'S','05.03.2016',-12,5) 
    ;WITH CTE 
    AS (
     SELECT stockid 
      , dealid 
      , dealtype 
      , stockdate 
      , priceperstock 
      , stockamount 
      , sum(stockamount) OVER (
       ORDER BY dealid DESC 
       ) AS runningtotal 
      , sum(stockamount) OVER (
      ORDER BY dealid) AS position 
     FROM #testtable 
     ) 
    SELECT stockid 
     , dealid 
     , dealtype 
     , stockdate 
     , priceperstock 
     , stockamount 
     --, runningtotal 
     , position 
     , CASE 
      WHEN dealtype = 'S' 
       THEN 0 
      WHEN stockamount > runningtotal AND runningtotal < 0 
       THEN 0 
      WHEN stockamount > runningtotal AND runningtotal >= 0 
       THEN runningtotal 
      WHEN stockamount < runningtotal 
       THEN stockamount 
      END AS StockRemaining 
    FROM cte 
    ORDER BY dealid