[SQL Server] Laatste records ophalen tot specifiek aantal

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • Tha-PC-Legend
  • Registratie: Augustus 2002
  • Nu online
In een SQL Server database heb ik een tabel waar alle transacties in aanwezig zijn van alle artikelen. Dat wil zeggen: Van iedere transactie (voorraad in of uit) is een record aanwezig. Laten we zeggen dat ik de volgende kolommen heb:

ArtikelcodeMagazijnMagazijnlocatieAantal (+ of -)Datum


Ik wil per artikel de transacties zien die zorgen voor de huidige voorraad (aantal) per magazijnlocatie. De huidige voorraad is dus de SUM van alle transacties per artikel/magazijn/magazijnlocatie. Dit zijn logischerwijs de laatste paar transacties die plaats hebben gevonden.

Voorbeeld: De volgende transacties hebben plaats gevonden voor Artikel XYZ:

ArtikelcodeMagazijnMagazijnlocatieAantal (+ of -)Datum
XYZ1A1001-10-2021
XYZ1A2002-10-2021
XYZ1A2003-10-2021
XYZ1A-1204-10-2021
XYZ1A1005-10-2021


De SUM van deze transacties is 48. Ik zou nu in mijn query graag alle transacties zien met een positief aantal, tot dat het aantal 48 is bereikt of overschreden. Dus, de transacties van 05-10-2021, 03-10-2021 en 02-10-2021. Dit zorgt samen voor 50. Derhalve wil ik dat de transactie van 01-10-2021 niet getoond wordt. Immers is deze voorraad verbruikt op 04-10-2021 conform het FIFO principe.

Ik ben al de hele dag bezig om het voor elkaar te krijgen, maar tevergeefs. Alvast dank voor je hulp! :)

Alle reacties


Acties:
  • +1 Henk 'm!

  • urk_forever
  • Registratie: Juni 2001
  • Laatst online: 29-09 15:54
Wil je dit in een query doen, of kan je ook een stored procedure of een table valued function gebruiken? In een sp/tvf zou je eerst de positieve en negatieve records op kunnen halen en dan vervolgens met een loop/cursor de negatieve records eraf kunnen trekken totdat je alle records gehad hebt en dan geeft je alleen de records terug die een waarde > 0 hebben.

In een query wordt het denk ik lastiger.

Hail to the king baby!


Acties:
  • +1 Henk 'm!

  • Paul!1987!
  • Registratie: April 2018
  • Laatst online: 20:41
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
create table test(id int not null primary key, aantal int not null)

insert into test values(1,10),(2,20),(3,20),(4,-12),(5,10);

select  id, aantal
from (
    select id, aantal, (select sum(aantal) 
                  from test T2 
                  where T2.id >= T1.id) as acc 
    from test T1 
) as tmp 
where acc < (select sum(aantal) from test)
and aantal >0

Acties:
  • 0 Henk 'm!

  • Tha-PC-Legend
  • Registratie: Augustus 2002
  • Nu online
Paul!1987! schreef op donderdag 7 oktober 2021 @ 16:01:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
create table test(id int not null primary key, aantal int not null)

insert into test values(1,10),(2,20),(3,20),(4,-12),(5,10);

select  id, aantal
from (
    select id, aantal, (select sum(aantal) 
                  from test T2 
                  where T2.id >= T1.id) as acc 
    from test T1 
) as tmp 
where acc < (select sum(aantal) from test)
and aantal >0
Bedankt voor je reactie. Jouw voorbeeld werkt inderdaad perfect :) Echter, wanneer ik deze ombouw naar een query voor mijn database, dan gaat het helaas niet goed. En ik kan maar niet vinden waar het in zit.

In betreffende tabel in mijn database zitten transacties voor alle artikelen. Ik moet met wat meer variabelen rekening houden dan in mijn eerste post aangegeven. Ik zag de relevantie in eerste instantie niet, daarom heb ik het weg gelaten. Maar misschien zijn deze toch belangrijk. Het betreft de transtype en reknr variabelen. Hieronder de query zoals ik deze nu gebruik:

code:
1
2
3
4
5
6
7
8
9
10
11
12
select id, artikel, aantal, datum
from (
select id, artikel, aantal, datum, (select sum(aantal)
from test T2
where T2.id >= T1.id and artikel = 'XYZ' and transtype = 'N' and ltrim(rtrim(reknr)) = '3000') as acc
from test T1
where artikel = 'XYZ'
and transtype = 'N'
and ltrim(rtrim(reknr)) = '3000'
) as tmp
where acc < (select sum(aantal) from test where artikel = 'XYZ' and transtype = 'N' and ltrim(rtrim(reknr)) = '3000')
and aantal >0


De SUM van deze transacties is 70. Desalniettemin wordt de volgende output gegenereerd:

Afbeeldingslocatie: https://tweakers.net/i/12ArsPDL3BhCEu7ZVp0hgINbRuM=/full-fit-in/4920x3264/filters:max_bytes(3145728):no_upscale():strip_icc():fill(white):strip_exif()/f/image/0gKRvoMUZEFUuo0iokGjyCgQ.jpg?f=user_large

De som hiervan is 75. Misschien komt het omdat de laatste transactie in deze lijst 24 is? In werkelijkheid is de eerste transactie die van 01-12-2019 (regel 6), en moet deze dus weg worden gelaten, want deze is al uit voorraad. Een kleine aanpassing? Bedankt!

Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 22:11

Creepy

Tactical Espionage Splatterer

Ja ho ff. Queries schrijven moeten we in principe zelf doen. Dus wat zou je zelf aanpassen om dat ene resultaat niet mee te krijgen? Ga er even iets verder mee aan de slag aub ipv om een aanpassing te vragen. Het is hier geen code afhaal balie.

[ Voor 29% gewijzigd door Creepy op 07-10-2021 16:47 ]

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • +2 Henk 'm!

  • Tha-PC-Legend
  • Registratie: Augustus 2002
  • Nu online
Ik ben er vanavond opnieuw voor gaan zitten (na reeds uren en uren hier aan gespendeerd te hebben gisteren en vandaag; het is niet zo dat ik nog niets geprobeerd heb. Integendeel). Met de suggestie hierboven krijg ik het helaas niet aan de praat. Ik heb het over een andere boeg gegooid: Een running total mee laten lopen en alle records opgehaald waarbij de running total < de som van alle transacties (de voorraad).

Je mist dan nog één record: Het record waar slechts een deel nog van op voorraad ligt. Ik heb een UNION toegepast en dat ene record in die query opgehaald. Zodoende heb ik nu alle records die ik wil hebben. Misschien niet heel efficiënt, maar de performance van de query is goed en de data klopt.

Bedankt voor het meedenken, Paul :)

Acties:
  • 0 Henk 'm!

  • hoi3344
  • Registratie: November 2011
  • Laatst online: 01-10 00:51
Ik zie trouwens dat je LTRIM en RTRIM gebruikt. sinds "SQL Server 2017 (14.x) and later" is er ook TRIM, die beiden in één heeft.
Verder vind ik de functionaliteit die je probeert te bereiken een beetje vreemd, maar zou via SQL zeker haalbaar moeten zijn. misschien met wat APPLY's of zelfs een recursieve CTE, om de 'running total' te bepalen. Het is echter vanuit zo'n stuk tekst lastig in te schatten wat de oplossing moet zijn, daarvoor zou ik zelf wat queries moeten schrijven.

Acties:
  • 0 Henk 'm!

  • nescafe
  • Registratie: Januari 2001
  • Laatst online: 21:12
De ltrim(rtrim(reknr)) = '3000' maakt je query inefficiënt.

Beter is (veld is rechts uitgelijnd in een varchar(9)):
reknr = '     3000'


Of je pakt een query uit één van de voorloopschermen (CTRL-Q in Globe) en gebruikt die als basis. Bijv. join naar Items ON gbkmut.artcode = Items.ItemCode AND gbkmut.reknr = Items.GLAccountDistribution houdt rekening met artikelen met een andere voorraadrekening dan 3000.

Tevens mis je in je transtype nog je beginbalans en correcties, hoewel ik dat in de praktijk nog nooit ben tegengekomen.. transtype IN ('X', 'N', 'C', 'P')

[ Voor 16% gewijzigd door nescafe op 14-10-2021 17:38 ]

* Barca zweert ook bij fixedsys... althans bij mIRC de rest is comic sans


Acties:
  • 0 Henk 'm!

  • Tha-PC-Legend
  • Registratie: Augustus 2002
  • Nu online
hoi3344 schreef op donderdag 14 oktober 2021 @ 17:04:
Ik zie trouwens dat je LTRIM en RTRIM gebruikt. sinds "SQL Server 2017 (14.x) and later" is er ook TRIM, die beiden in één heeft.
Verder vind ik de functionaliteit die je probeert te bereiken een beetje vreemd, maar zou via SQL zeker haalbaar moeten zijn. misschien met wat APPLY's of zelfs een recursieve CTE, om de 'running total' te bepalen. Het is echter vanuit zo'n stuk tekst lastig in te schatten wat de oplossing moet zijn, daarvoor zou ik zelf wat queries moeten schrijven.
Bedankt voor de tip. Ik zal TRIM eens gaan proberen :)

Acties:
  • 0 Henk 'm!

  • Tha-PC-Legend
  • Registratie: Augustus 2002
  • Nu online
nescafe schreef op donderdag 14 oktober 2021 @ 17:10:
De ltrim(rtrim(reknr)) = '3000' maakt je query inefficiënt.

Beter is (veld is rechts uitgelijnd in een varchar(9)):
reknr = '     3000'


Of je pakt een query uit één van de voorloopschermen (CTRL-Q in Globe) en gebruikt die als basis. Bijv. join naar Items ON gbkmut.artcode = Items.ItemCode AND gbkmut.reknr = Items.GLAccountDistribution houdt rekening met artikelen met een andere voorraadrekening dan 3000.

Tevens mis je in je transtype nog je beginbalans en correcties, hoewel ik dat in de praktijk nog nooit ben tegengekomen.. transtype IN ('X', 'N', 'C', 'P')
In het eindresultaat join ik inderdaad op items.glaccountdistribution en items.itemcode en gebruik ik transtype in (‘N’, ‘C’, ‘P’, ‘X’). Ik wilde het voorbeeld zo simpel mogelijk houden ;)

De voorloopschermen bieden niet echt het gewenste overzicht. Dat had ik reeds geprobeerd. Het ‘Ouderdom voorraad’ overzicht komt het meeste in de buurt. Die query heb ik gevangen middels de Profiler, maar bleek uiteindelijk niet bruikbaar.

Anyway, de eigen query werkt naar wens en doet zijn werk! Bedankt voor je feedback :)

Acties:
  • 0 Henk 'm!

  • Skinny
  • Registratie: Januari 2000
  • Laatst online: 19-09 16:45

Skinny

DIRECT!

Zelf gebruik ik in dit soort situaties vaak PARTITION BY icm OVER().

Op een (versimpelde) voorraad mutatie tabel :

Afbeeldingslocatie: https://i.imgur.com/tXTK3zc.png

Kun je dan alle relevantie mutaties (voor product en lokatie) selecteren met een positief aantal als mutatie :

code:
1
2
3
4
5
SELECT ProductID, OrganizationUnitID, Quantity, CreationTime
From ProductLedger 
WHERE ProductID = 41693
AND OrganizationUnitID = 48
AND Quantity > 0


Vervolgens kun je aan die result zet een 'running total' toevoegen :

code:
1
2
3
4
5
SELECT ProductID, OrganizationUnitID, Quantity, CreationTime, SUM (Quantity) OVER (PARTITION BY ProductID, OrganizationUnitID ORDER BY CreationTime) AS RunningTotal
From ProductLedger 
WHERE ProductID = 41693
AND OrganizationUnitID = 48
AND Quantity > 0


Hiermee krijg je het volgende resultaat :

Afbeeldingslocatie: https://i.imgur.com/9WjWwxL.png

Nu ga je daar nog de limiet aan toevoegen tot waar je wil zoeken. Dit moet helaas met een subquery omdat PARTITION BY niet in een WHERE mag voorkomen.

code:
1
2
3
4
5
6
7
8
9
10
11
SELECT * FROM  (

SELECT ProductID, OrganizationUnitID, Quantity, CreationTime, SUM (Quantity) OVER (PARTITION BY ProductID, OrganizationUnitID ORDER BY CreationTime) AS RunningTotal
From ProductLedger 
WHERE ProductID = 41693
AND OrganizationUnitID = 48
AND Quantity > 0

) source
WHERE RunningTotal <=80
ORDER BY CreationTime


Afbeeldingslocatie: https://i.imgur.com/iDK8ewm.png

Hierboven selecteer ik dus alle mutaties met positieve aantallen tot en met een totaal van 80. Of je je datum ASC of DESC wil sorteren zal je zelf even moeten bepalen aan de hand van de business vraag. Ik denk dat je hiermee het wel kan vertalen naar jouw situatie. Succes!


Meer info over PARTITON BY : https://codingsight.com/c...-by-clause-in-sql-server/

SIZE does matter.
"You're go at throttle up!"

Pagina: 1