Opening Stock, Closing Stock By Date sql Query - sql

I have two table with primary key and foreign key (MaterialId)
Material Table (Multiple Material)
MaterialId MaterialName OpeningStock
1 Pen 100
2 Pencil 50
Material Stock (Multiple Material Entry)
MaterialId PurchaseQty SalesQty Date
1 500 0 2016-12-15
1 0 0 2016-12-16
1 300 0 2016-12-17
1 0 400 2016-12-18
1 0 0 2016-12-19
1 0 0 2016-12-20
1 0 400 2016-12-21
1 200 100 2016-12-22
Now When I Pass #FromDate and #Todate
I want to output like below:
Date MaterialName OpeningStock PurchaseQty SalesQty ClosingStock
2016-12-15 Pen 100 500 0 600
2016-12-16 Pen 600 0 0 600
2016-12-17 Pen 600 300 0 900
2016-12-18 Pen 900 0 400 500
2016-12-19 Pen 500 0 0 500
2016-12-20 Pen 500 0 0 500
2016-12-21 Pen 500 0 400 100
2016-12-22 Pen 100 200 100 200
Note :
1. If Something is wrong on database tables so, please guide me how to handle this situation.
2. And Also find Current Date Stock From Two Tables

You are looking for a rolling sum of the various quantity values. One way to do this is using correlated subqueries:
SELECT
t1.Date,
mt.MaterialName,
(SELECT OpeningStock FROM [Material Table] WHERE MaterialId = t1.MaterialId) +
COALESCE((SELECT SUM(t2.PurchaseQty - t2.SalesQty) FROM [Material Stock] t2
WHERE t2.Date < t1.Date AND t1.MaterialId = t2.MaterialId), 0) AS OpeningStock,
t1.PurchaseQty,
t1.SalesQty,
(SELECT OpeningStock FROM [Material Table] WHERE MaterialId = t1.MaterialId) +
COALESCE((SELECT SUM(t2.PurchaseQty - t2.SalesQty) FROM [Material Stock] t2
WHERE t2.Date <= t1.Date AND t1.MaterialId = t2.MaterialId), 0) AS ClosingStock
FROM [Material Stock] t1
INNER JOIN [Material Table] mt
ON t1.MaterialId = mt.MaterialId
ORDER BY
mt.MaterialName,
t1.Date;
Note that it is bad table design to be storing the opening stock values in a separate table from the material stock table. This means the above query would return no pencil records. A better approach would be to insert a seed record into material stock, for each material, with the amount being the initial stock.
Output:
Demo here:
Rextester

Simply do as below :
SELECT S.DATE, M.MaterialName, M.OpeningStock, S.PurchaseQty, S.SalesQty, SUM((M.OpeningStock+S.PurchaseQty)-S.SalesQty)ClosingStock FROM #TABLE
(
SELECT * FROM MaterialTABLE
) M
INNER JOIN Material S ON S.MaterialId = M.MaterialId where s.date between #FromDate and #Todate

Related

Cumulative vs. Tiered Calculation in SQL Query or Inline Table Value Function with Conditional Logic?

I'm working with two tables of data, but different methodologies to derive two distinct desired outputs below.
The first is a cumulative tier calculation and the second is a just a tiered lookup based on a range. I need to be able to return this calculation for a row item transaction based on some JOINS using foreign keys to other dimension tables, namely Accounts and Regions and Tier Type. For example, a single account could have a calculation be either cumulative or tiered based on the region's and/or account's ID.
Link to DB Fiddle
Type ID
Name
1
Cumulative
2
Tiered
Tiered table:
Account ID
Type ID
Region ID
TierNo
Min
Max
Total A
Total B
101
1
2
1
0
10000
.90
.10
101
1
2
2
10001
30000
.60
.40
101
1
2
3
30001
100000
.40
.60
101
1
2
4
100001
500000
.40
.60
101
1
2
5
500001
999999999999
.20
.80
102
1
3
1
0
7800
.80
.20
102
1
3
2
7801
12800
.70
.30
102
1
3
3
12801
34000
.60
.40
102
1
3
4
34001
50000
.50
.50
102
1
3
5
5000 1
999999999999
.50
.50
103
2
1
1
0
10000
.90
.10
103
2
1
2
10001
30000
.60
.40
103
2
1
3
30001
100000
.40
.60
103
2
1
4
100001
500000
.40
.60
103
2
1
5
500001
999999999999
.20
.80
Current table sample:
Trans ID
Account ID
Type ID
Region ID
GrossAmt
Total A %
Total A $
Net Amt
100001
101
1
2
42650
100002
102
1
3
42650
100003
103
2
1
42650
Desired output:
Trans ID
Account ID
Type ID
Region ID
GrossAmt
Total A %
Total A $
Net Amt
100001
101
1
2
42650
0.611
26059.99
16589.99
100002
102
1
3
42650
0.628
26784.98
15864.99
100003
103
2
1
42650
0.40
17060.00
25590.00
I've been able to make some edits to a previous post to get the accounts, but can't seem to figure out the logic for the tiered lookup value in TransID 100003.
Ideally, I'd prefer to create this logic in a table value function (or two) and then incorporate it into a View that I'll use for reporting in a web form using C#.
SELECT
c.*,
[Total A %] = t.Total / c.GrossAmt,
[Total A $] = t.Total,
[Net Amt] = c.GrossAmt - t.Total
FROM #temp c
INNER JOIN Accounts a ON a.[Account ID] = c.[Account ID]
CROSS APPLY (
SELECT
Total = SUM((v.ActualMax - t.[Min]) * t.[Total A %])
FROM [dbo].[Tiered Table] t
CROSS APPLY (VALUES(
CASE WHEN c.GrossAmt < t.[Max] THEN c.GrossAmt ELSE t.[Max] END
)) v(ActualMax)
WHERE c.GrossAmt > t.[Min] AND t.[Account ID] = c.[Account ID]
) t;
Any ideas or guidance would be extremely helpful and appreciated.
It seems to be a simple matter of AND OR logic. You need to exclude rows which are TypeID = 2 and also have their maximum below the level of GrossAmt.
Then you just conditionally aggregate either the total amount (for tiered rows only, there will be only one row) or just the amount for that tier (for cumulative tiers).
SELECT
c.*,
[Total A %] = t.Total / c.GrossAmt,
[Total A $] = t.Total,
[Net Amt] = c.GrossAmt - t.Total
FROM CurrentData c
INNER JOIN Accounts a ON a.[AccountID] = c.[AccountID]
CROSS APPLY (
SELECT
Total = SUM(CASE WHEN t.TypeID = 2 THEN v.GrossAmt ELSE (v.ActualMax - t.[Min]) END * t.[Total A])
FROM [dbo].[Tiers] t
CROSS APPLY (VALUES(
CASE WHEN c.GrossAmt < t.[Max] THEN c.GrossAmt ELSE t.[Max] END,
c.GrossAmt
)) v(ActualMax, GrossAmt)
WHERE t.[AccountID] = c.[AccountID]
AND t.TypeID = c.TypeID
AND t.RegionID = c.RegionID
AND c.GrossAmt > t.[Min]
AND (t.TypeID = 1 OR c.GrossAmt <= t.Max)
) t;
db<>fiddle
The second CROSS APPLY is only necessary because of aggregating outer values. You don't need this if you place it in a function, as shown in your previous question.
Note that you should use half-open intervals here. In other words, either Min or Max should be exclusive. Otherwise there may be values that can "fall through the cracks".

Dividing a sum value into multiple rows due to field length constraint

I am migrating financial data from a very large table (100 million+ of rows) by summarizing the amount and insert them into summary table. I ran into problem when the summary amount (3 billions) is larger than what the field in the summary table can hold (can only hold up to 999 millions.) Changing the field size is not an option as it requires a change process.
The only option I have is to divide the amount (the one that breach the size limit) into smaller ones so it can be inserted into the table.
I came across this SQL - I need to divide a total value into multiple rows in another table which is similar except the number of rows I need to insert is dynamic.
For simplicity, this is how the source table might look like
account_table
acct_num | amt
-------------------------------
101 125.00
101 550.00
101 650.00
101 375.00
101 475.00
102 15.00
103 325.00
103 875.00
104 200.00
104 275.00
The summary records are as follows
select acct_num, sum(amt)
from account_table
group by acct_num
Account Summary
acct_num | amt
-------------------------------
101 2175.00
102 15.00
103 1200.00
104 475.00
Assuming the maximum value in the destination table is 1000.00, the expected output will be
summary_table
acct_num | amt
-------------------------------
101 1000.00
101 1000.00
101 175.00
102 15.00
103 1000.00
103 200.00
104 475.00
How do I create a query to get the expected result? Thanks in advance.
You need a numbers table. If you have a handful of values, you can define it manually. Otherwise, you might have one on hand or use a similar logic:
with n as (
select (rownum - 1) as n
from account_table
where rownum <= 10
),
a as (
select acct_num, sum(amt) as amt
from account_table
group by acct_num
)
select acct_num,
(case when (n.n + 1) * 1000 < amt then 1000
else amt - n.n * 1000
end) as amt
from a join
n
on n.n * 1000 < amt ;
A variation along these lines might give some ideas (using the 1,000 of your sample data):
WITH summary AS (
SELECT acct_num
,TRUNC(SUM(amt) / 1000) AS times
,MOD(SUM(amt), 1000) AS remainder
FROM account_table
GROUP BY acct_num
), x(acct_num, times, remainder) AS (
SELECT acct_num, times, remainder
FROM summary
UNION ALL
SELECT s.acct_num, x.times - 1, s.remainder
FROM summary s
,x
WHERE s.acct_num = x.acct_num
AND x.times > 0
)
SELECT acct_num
,CASE WHEN times = 0 THEN remainder ELSE 1000 END AS amt
FROM x
ORDER BY acct_num, amt DESC
The idea is to first build a summary table with div and modulo:
ACCT_NUM TIMES REMAINDER
101 2 175
102 0 15
103 1 200
104 0 475
Then perform a hierarchical query on the summary table based on the number of "times" (i.e. rows) you want, with an extra for the remainder.
ACCT_NUM AMT
101 1000
101 1000
101 175
102 15
103 1000
103 200
104 475

Calculating a difference for an item, between most recent dates

I'm looking to calculate the difference between weights of my loaded trucks everyday. Basically, I want to know the incremental weight amounts carried each day. In theory, the trucks will be running on a daily basis, but I'm using a simplified data set on my mock database.
This is the code I've come up with:
--create table dbo.truck
--(TruckID int, Weight float, datetrunc date, weightdiff float)
declare #dt1 datetime, #dt2 datetime
select #dt2 = max(datetrunc) from truck
select #dt1 = max(datetrunc) from truck where datetrunc < #dt2
select #dt1 [dt1], #dt2 [dt2]
SELECT t1.truckid, t2.weight - t1.weight [WeightDiff]
FROM truck t1
inner join truck t2 ON t1.truckid = t2.truckid
WHERE t1.datetrunc = #dt1
AND t2.datetrunc = #dt2
UPDATE truck SET WeightDiff = x.WeightDiff
FROM (
SELECT t1.truckid, t2.weight - t1.weight [WeightDiff]
FROM truck t1
inner join truck t2 ON t1.truckid = t2.truckid
WHERE t1.datetrunc = #dt1
AND t2.datetrunc = #dt2
) AS X
WHERE truck.datetrunc = #dt2
AND x.truckid = truck.truckid
SELECT t1.truckid, #dt2, t2.weight - t1.weight
FROM truck t1
inner join truck t2 ON t1.truckid = t2.truckid
WHERE t1.datetrunc = #dt1
AND t2.datetrunc = #dt2
I'm hoping for a difference between each date to show. However, it is only dsiplaying the difference between the latest dates, while deleting&null the other rows:
TruckID Weight datetrunc weightdiff
1 1000 2019-03-01 NULL
2 1111 2019-03-01 NULL
3 1222 2019-03-01 NULL
1 1050 2019-03-15 NULL
2 1700 2019-03-15 NULL
3 1400 2019-03-15 NULL
1 1125 2019-03-31 75
2 1725 2019-03-31 25
3 1600 2019-03-31 200
I want:
TruckID Weight datetrunc weightdiff
1 1000 2019-03-01 NULL
2 1111 2019-03-01 NULL
3 1222 2019-03-01 NULL
4 1400 2019-03-01 NULL
1 1050 2019-03-15 50
2 1700 2019-03-15 589
3 1400 2019-03-15 178
4 1490 2019-03-15 90
1 1125 2019-03-31 75
2 1725 2019-03-31 25
3 1600 2019-03-31 200
4 1900 2019-03-31 510
Notice how some TruckID 4 were completed deleted from my dataset. Also, how it replaces the weight difference.
I can't seem to figure how to make my data display properly. Any suggestions help, thanks!
For SQL Server 2008
select a.truckid, a.weight, a.datetrunc, (a.weight - c.weight) weightdiff
from truck a
outer apply (select top 1 weight from truck b where b.datetrunc<a.datetrunc and b.truckid=a.truckid order by b.datetrunc desc) c
Newer SQL Server
select truckid, weight, datetrunc, weight - lag(weight) over (partition by truckid order by datetrunc) weightdiff
from truck

SQL Group by range and total column

With the following function and stored procedure i get a resultset with 2 columns.
What i need additional is a third column total for all open invoices inner score range
It would great for any idea.
ALTER FUNCTION dbo.OpenOrders
(
#MandantId int
)
RETURNS TABLE
AS
RETURN
SELECT SUM(Invoices.Amount - ISNULL(Payment.Amount, 0)) AS Op
FROM Invoices INNER JOIN
Orders ON Invoices.OrderId = Orders.OrderId LEFT OUTER JOIN
Payment ON Invoices.InvoiceId = Payment.InvoiceId
WHERE (Orders.MandantId = #MandantId)
GROUP BY Invoice.InvoiceId, Invoices.Amount
HAVING (SUM(Invoices.Amount - ISNULL(Payment.Amount, 0)) <> 0)
ALTER PROCEDURE dbo.GetOpRanges
#MandantId int
AS
BEGIN
SELECT * INTO #tmp_ranges
FROM
//wrong in first post -> OPDebitorByMandant(#MandantId)
OpenOrders(#MandantId)
SELECT op AS [score range], COUNT(*) AS [number of occurences]
FROM
(SELECT CASE
WHEN op BETWEEN 0 AND 50 THEN ' 0- 50'
WHEN op BETWEEN 50 AND 100 THEN ' 50-100'
WHEN op BETWEEN 100 AND 500 THEN '100-500'
ELSE '500 and >' END AS op
FROM [#tmp_ranges]) AS t
GROUP BY op
RETURN
Result:
score range number of occurences range
------------------+-------------
0- 50 23
50-100 4
100-500 4
500 and > 21
What i need additional is a third column total for all open invoices inner score range.
Target result:
score range number of occurences Total
-----------+--------------------+------
0- 50 23 1.150
50-100 4 400
100-500 4 2.000
500 and > 21 22.000
Tables:
Invoices
InvoiceId CustomerId OrderId DateOfInvoice Amount
----------+----------+-------+-------------+------
1 1 20 20160301 1000.00
2 2 22 20160501 2000.00
3 1 102 20160601 3000.00
...
Orders
OrderId MandantId CustomerId DateOfOrder Amount
-------+---------+----------+-----------+-----------
20 1 1 20160101 1000.00
22 1 2 20160101 2000.00
102 1 1 20160101 3000.00
...
Payment
PaymentId MandantId CustomerId InvoiceId OrderId DateOfPayment Amount
---------+---------+----------+---------+-------+-------------+-------------
1 1 1 1 20 20160310 1000.00
2 1 2 2 22 20160505 2000.00
3 1 1 3 102 20160610 3000.00
...
hope it's helpfull and thanks again in advance for any solution

Group values in to categories with annual break down

I have a table of licence applications I want to display the data by category for each financial year.
For my query, there are 2 key columns.
Firstly, there is a fee column and the values within this column determine the type of licence.
Between 0 and 300 is Minor
between 300 and 600 is Standard
between 600 and 2000 is Major
Secondly, there is a date field which is to be used for the financial year.
I would like the results to look like this.
Category | 2013/14 | 2012/13
Minor | 23 | 21
Standard | 10 | 11
Major | 5 | 3
I have this query below, but i cant get it right for the year part.
Would really appreciate any advice people can give me.
select category.gr as [category],
sum(case when ((year(licence.[start_date]) in ('2010'))
and (month(licence.[start_date]) in (4,5,6,7,8,9,10,11,12)))
or ((year(licence.[start_date]) in ('2011'))
and (month(licence.[start_date]) in (1,2,3))) then 1 else 0 end) AS '10/11 Count',
from ( select case
when [fee_INC] between 0 and 350 then 'Minor'
when [fee_INC] between 350 and 600 then 'Standard'
else 'Major' end as gr
from [L_LICENCE_FIN]) as category,
from [L_LICENCE_FIN] as licence
group by category.gr
SELECT
[category],
[2013/14],
[2012/13]
FROM (
SELECT
[category],
STR(YEAR(DATEADD(month,-3,[start_date])),4)
+'/'
+RIGHT(STR(YEAR(DATEADD(month,-3,[start_date]))+1,4),2)
AS [fiscal_year],
COUNT(*) AS [count]
FROM #L_LICENCE_FIN
INNER JOIN (VALUES
( 0, 300, 'Minor'),
(300, 600, 'Standard'),
(600,2000, 'Major')
) categories([fee_min], [fee_max], [category])
ON ([fee] >= [fee_min] AND [fee] < [fee_max])
GROUP BY [category],[start_date]
) p1
PIVOT(SUM([count]) FOR [fiscal_year] IN ([2013/14],[2012/13])) p2