Calculate average rate in SQL Server - sql

declare #table as table
(
LedgerId int,
Transaction_Type varchar(50),
Inward_qty decimal (18,3),
Inward_rate decimal (18,3)
)
insert into #table
values (1, 'Issue', 67.320, 473.66),
(2, 'Receipt', 201.290, 657.90),
(3, 'Receipt', 94.860, 473.66)
select * from #table
I want to calculate the average rate the formula is finding the receipt and subtract from issue remaining will be multiplied by the rate we will have the value than total value divided by total receipt qty and we will get an inward rate I have no clue how to do this
Formula
201.290 - 67.320 = 133.970 (receipt - issue)
133.970 * 657.900 = 88,138.863(remaining receipt qty * rate) will get value
94.860 * 473.660 = 44,931.3876(receipt * rate) will get value
88,138.863 + 44,931.3876 = 1,33,070.2506 (sum of total value)
133.970 + 94.860 = 228.83(sum of total qty )
1,33,070.2506 / 228.83 (sum of total value) / (sum of total qty )
= 581.5244967880086 thus 581.524 is average rate
output layout as per above calculation
declare #table1 as table
(
LedgerId int,
Transaction_Type varchar(50),
Inward_qty decimal (18,3),
Inward_rate decimal (18,3)
)
insert into #table1 values(1,'Issue',67.320,473.66),(2,'Receipt',133.970,657.90),(3,'Receipt',94.860,473.66)
select * , inward_Value = Inward_qty * Inward_rate into #temp from #table1
declare #Qty decimal (18,3)
declare #value decimal (18,3)
set #Qty= (select sum(Inward_qty) from #temp where Transaction_Type ='Receipt' )
set #value =(select sum(inward_Value) from #temp where Transaction_Type ='Receipt' )
declare #Rate decimal (18,3) = #Value/#Qty
select #Rate
drop table #temp

Related

Calculate column value based on criterias (values from both current row and a previous one)

I am trying to learn some SQL by combining it with my interest in finance. At the moment I am attempting to calculate some portfolio data based on a list of transactions (in SQL Server).
This is my table:
create table StockTransactions
(
TransactionID int,
Instrument varchar(32),
TransactionType varchar(32),
Units int,
Price float,
TransactionSum as case
when TransactionType = 'Buy' then (-(Units * Price))
when TransactionType = 'Sell' then (Units * Price)
else 0
end,
PurchaseCost float
)
Sample data:
insert into StockTransactions (TransactionID, Instrument, TransactionType, Units, Price)
values
(1, 'Apple', 'Buy', 10, 120),
(2, 'Microsoft', 'Buy', 20, 290),
(3, 'Apple', 'Buy', 10, 125),
(4, 'Apple', 'Sell', 5, 140),
(5, 'Apple', 'Buy', 10, 130),
(6, 'Apple', 'Sell', 10, 135)
Which results in this table:
TransactionID
Instrument
TransactionType
Units
Price
TransactionSum
PurchaseCost
1
Apple
Buy
10
120
-1200
NULL
2
Microsoft
Buy
20
290
-5800
NULL
3
Apple
Buy
10
125
-1250
NULL
4
Apple
Sell
5
140
700
NULL
5
Apple
Buy
10
130
-1300
NULL
6
Apple
Sell
10
135
1350
NULL
I am trying to calculate the values for the PurchaseCost column. The values I want are:
Row
PurchaseCost
1
-1200
2
-5800
3
-2450
4
-1837.5
5
-3137.5
6
-2353.13
The logic is as follows. If a purchase has been made, the TransactionSum should be added to the latest previous PurchaseCost for that instrument. However, when a sale has been made a certain sum should be subtracted.
In transaction four I sell 5 of my 20 Apple shares. Therefor I'd like to subtract 1/4 of the previous PurchaseCost for a new PurchaseCost value of -1837.5.
In transaction six I sell 10 of my 25 Apple shares, and would then like to reduce the PurchaseCost to 2353.13 (60% of the previous value).
I can't seem to figure out how to do this by myself. Any ideas?
You can create store procedure to calculate each row
Here, the code to calculate and select using temporary table
IF OBJECT_ID('tempdb..#TempStock') IS NOT NULL DROP TABLE #TempStock
DECLARE #TransId int
DECLARE #TempInst varchar(32)
DECLARE #TempPurchase float
DECLARE #TempUnit int
DECLARE #TempTrans varchar(32)
DECLARE #LastPurchase float
DECLARE #LastUnits int
CREATE TABLE #TempStock (
TransactionID int,
Instrument varchar(32),
Units int,
PurchaseCost float
)
DECLARE MY_CURSOR CURSOR
FOR SELECT DISTINCT TransactionID
FROM StockTransactions
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO #TransId
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #TempInst = Instrument, #TempTrans = TransactionType, #TempUnit = Units, #TempPurchase = TransactionSum FROM StockTransactions
WHERE TransactionID = #TransId
SELECT TOP 1 #LastPurchase = PurchaseCost, #LastUnits = Units
FROM #TempStock
WHERE Instrument = #TempInst
ORDER BY TransactionID DESC
IF ##ROWCOUNT = 0
BEGIN
SELECT #LastPurchase = 0, #LastUnits = 0
END
IF #TempTrans = 'Buy'
BEGIN
INSERT INTO #TempStock (TransactionID, Instrument, Units, PurchaseCost)
VALUES (
#TransId,
#TempInst,
ISNULL(#LastUnits,0) + ISNULL(#TempUnit,0),
ISNULL(#LastPurchase,0) + ISNULL(#TempPurchase,0)
)
END
ELSE IF #TempTrans = 'Sell'
BEGIN
INSERT INTO #TempStock (TransactionID, Instrument, Units, PurchaseCost)
VALUES (
#TransId,
#TempInst,
ISNULL(#LastUnits,0) - ISNULL(#TempUnit,0),
(ISNULL(#LastPurchase,0) * (ISNULL(#LastUnits,0) - ISNULL(#TempUnit,0))) / ISNULL(#LastUnits,0)
)
END
FETCH NEXT FROM MY_CURSOR INTO #TransId
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR
SELECT TransactionID, PurchaseCost FROM #TempStock
DROP TABLE #TempStock
You can update the master table from that temporary table

Amount Calculation using Precision

I have a salary table which has Amount and Amount Precision columns.
Using precision I want to get the actual amount.
Please help me to calculate actual amount using precision.
use POWER(). The multiplication with 1.0 is required to convert your Amount in integer into decimal
ActualAmount = Amount * 1.0 / Power(10, AmountPrecision)
You can use the following query:
select CAST(Amount as double precision) / power(10, AmountPrecision) from AmountTest
Assuming AmountTest is the name of the table. You can replace it with the name given by you.
DECLARE #T TABLE (
Amount INT,
AmountPrecision INT
)
INSERT INTO #T VALUES(1,1),(51,1),(51,2),(934,3),(1024,2)
SELECT
*,
CAST(AMOUNT AS FLOAT)/(CONCAT(1,REPLICATE(0,AMOUNTPRECISION))) AS ACTUALAMOUNT
FROM #T
You can also try the divide by just creating value using REPLICATE() function.
CREATE TABLE Data (id int, AmountPrecision int)
INSERT INTO Data (id, AmountPrecision) VALUES
(1, 1)
, (51, 1)
, (51, 2)
, (934, 3)
, (1024, 2)
Select id
, Cast('1' + REPLICATE('0', AmountPrecision) as int) as DivideBy
, Cast(id * 1.0 / Cast('1' + REPLICATE('0', AmountPrecision) as int)
as float) as FormattedNumber
from Data
Live db<>fiddle demo.

T-SQL INSERT - Increment value when other columns are duplicated

I have the following table:
CREATE TABLE Budget (
Year Int,
Region varchar(50),
Amount float,
DraftNo int);
GO
INSERT INTO Budget
VALUES
(2018, 'Region1', 500000, 1),
(2018, 'Region2', 400000, 1),
(2018, 'Region3', 300000, 1);
End users will submit data for Year, Region, and Amount through an Excel form, which will use VBA to write INSERT statements against the table. DraftNo will be 1 by default, but if there is already a match in the table on the first two columns (Year and Region), I want to increment DraftNo by one each time.
For example, if the application tries to write:
INSERT INTO Budget VALUES (2018, 'Region1', 600000, 1)
It should be converted to:
INSERT INTO Budget VALUES (2018, 'Region1', 600000, 2)
Of course, the solution will also need to recognize the max draft number for the Year/Region combination and increment one more from there, rather than always using 2.
If you need to add new record with incremental DraftNo:
declare #y int = 2018, #r varchar(50) = 'Region6', #a float = 3
INSERT INTO Budget VALUES (#y, #r, #a, isnull((select max(DraftNo) from Budget where [Year] = #y and Region = #r), 0) + 1)
If you need update existing records and add DraftNo:
Using MERGE:
merge Budget as trg
using (select 2018, 'Region1', 600000) as src (y, r, a)
on trg.[Year] = src.y and trg.[Region] = src.r and trg.Amount = src.a
when matched then
update set DraftNo = DraftNo + 1
when not matched then
insert ([Year], Region, Amount, DraftNo)
values (y, r, a, 1);
Using Update + Insert:
declare #y int = 2018, #r varchar(50) = 'Region6', #a float = 3
update Budget
set DraftNo = DraftNo + 1
where [Year] = #y and [Region] = #r and Amount = #a
if ##ROWCOUNT = 0
insert Budget (Year, Region, Amount, DraftNo)
values (#y, #r, #a, 1)

Replace cursors with queries

Let's say I have a booking covering 6 hours and 3 discounts covering 2 hours each. I want to split my booking into 3 parts so I can allocate 2 hours per discount.
It would return something like this:
BookingId 1 | DiscountId 1 | Qty 2
BookingId 1 | DiscountId 2 | Qty 2
BookingId 1 | DiscountId 3 | Qty 2
I would then insert those records this into another table.
I'm using an heavily optimized query to determine the number of hours available for each discount. However, I can't find a "good" way to allocate my booking to each discount without using a cursor.
(...)
WHILE ##FETCH_STATUS = 0
BEGIN
IF #RequiredQty = 0
RETURN
IF #RequiredQty <= #AvailableQty
BEGIN
INSERT INTO discount.Usage (DiscountId, BookingId, Quantity)
VALUES (#DiscountId, #BookingId, #RequiredQty)
SET #RequiredQty = 0
END
IF #RequiredQty > #AvailableQty
BEGIN
INSERT INTO discount.Usage (DiscountId, BookingId, Quantity)
VALUES (#DiscountId, #BookingId, #AvailableQty)
SET #RequiredQty -= #AvailableQty
END
FETCH NEXT FROM ecursor INTO #DiscountId, #AvailableQty
END
DEALLOCATE ecursor
I tried building the corresponding query but I can't select and assign variables at the same time. Using a cursor is not really a problem (besides some potential performance issues) but I was just curious to see if with the newest SQL Server we can convert our old cursors to something better?
Thanks,
Seb
You can useCTE RECURSIVE to make a Table.
like this.
DECLARE #BookingId INT = 1;
DECLARE #RequiredQty INT = 2;
DECLARE #Hours INT = 7;
CREATE TABLE #T
(
BookingId INT,
DiscountId INT,
Quantity INT
)
;WITH CTE([Count],[Quantity],Rk) AS
(
SELECT
CASE
WHEN [HOURS] - #RequiredQty > #RequiredQty THEN #RequiredQty
ELSE [HOURS] - #RequiredQty
END ,
T.HOURS,1
FROM
(
SELECT #Hours [HOURS]
) AS T
UNION ALL
SELECT CASE
WHEN CTE.[Quantity] - #RequiredQty > #RequiredQty THEN #RequiredQty
ELSE CTE.[Quantity] - #RequiredQty
END AS [Count],
CTE.[Quantity] - #RequiredQty,
RK + 1
FROM CTE
WHERE CTE.[Quantity] - #RequiredQty > 0
)
INSERT INTO #T(BookingId,DiscountId,Quantity)
SELECT #BookingId,Rk,[Count] FROM CTE
option (maxrecursion 0)
select * from #T
SQLDEMO
This is another approach, but don't know if this code has better performance than cursor.
DECLARE #DiscountStocks TABLE (Id INT IDENTITY(1,1), DiscountId INT, LastQty INT)
INSERT INTO #DiscountStocks (DiscountId, LastQty) VALUES (1, 5)
INSERT INTO #DiscountStocks (DiscountId, LastQty) VALUES (2, 2)
INSERT INTO #DiscountStocks (DiscountId, LastQty) VALUES (3, 1)
DECLARE #DiscountBookings TABLE (Id INT IDENTITY(1,1), DiscountId INT, BookingId INT, Qty INT)
DECLARE #BookingDiscount TABLE (Id INT IDENTITY(1,1), BookingId INT, DiscountId INT, Qty INT)
INSERT INTO #BookingDiscount (BookingId, DiscountId, Qty) VALUES (1, 1, 4)
INSERT INTO #BookingDiscount (BookingId, DiscountId, Qty) VALUES (1, 2, 2)
INSERT INTO #BookingDiscount (BookingId, DiscountId, Qty) VALUES (1, 3, 1)
INSERT INTO #BookingDiscount (BookingId, DiscountId, Qty) VALUES (2, 1, 1)
INSERT INTO #BookingDiscount (BookingId, DiscountId, Qty) VALUES (2, 2, 2)
SELECT BD.Id AS BDId, DS.Id AS DSId, DS.LastQty, BD.Qty
, DS.LastQty - (SELECT SUM(Qty) FROM #BookingDiscount WHERE Id <= BD.Id AND DiscountId = BD.DiscountId) AS QtyAfterSubstract
INTO #LastDiscountStock
FROM #DiscountStocks DS
INNER JOIN #BookingDiscount BD ON DS.DiscountId = BD.DiscountId
ORDER BY BD.Id, DS.Id
INSERT INTO #DiscountBookings (DiscountId, BookingId, Qty)
SELECT DSId, BDId, Qty
FROM #LastDiscountStock
WHERE QtyAfterSubstract >= 0
DROP TABLE #LastDiscountStock
SELECT * FROM #DiscountBookings

SQL rounding to decimal places

I have a dataset which i need to do a calculation on price and round to decimal places. But the results aren't quite what is expected. The calculation is in the case statement
CREATE TABLE #Temp ( ID INT IDENTITY(1,1), Price DECIMAL(7,2) )
INSERT INTO #TEMP ( Price )
VALUES ( 119.99 )
, ( 48.99 )
SELECT
ID
, Price
, CASE WHEN Price > 10 THEN CONVERT( DECIMAL(7,2), Price * 1.08 - 0.05 ) END AS RRP
FROM #Temp
DROP TABLE #Temp
with the results
ID Price RRP
1 119.99 129.54
2 48.99 52.86
I need to get the 129.54 to 129.55 and the 52.86 to 52.85 within the same case statement if that is possible to match up with another data set
Are you rounding to the nearest 5 cents because your country has gotten rid of the penny? This is the only logic that we can seem to follow with your expected results.
SELECT ID
,Price
,CASE
WHEN Price > 10
THEN ROUND(CONVERT(DECIMAL(7, 2), Price * 1.08 - 0.05) * 20, 0) / 20
END AS RRP
FROM #Temp
Assuming you want to round to the nearest 5 cents or nearst 5 in 100ths decimal position.
INSERT INTO #TEMP ( Price )
VALUES ( 119.99 )
, ( 48.99 )
SELECT
ID
, Price
, Price * 1.08 - 0.05
, CASE WHEN Price > 10 THEN CONVERT( DECIMAL(7,2), round((Price * 1.08 - 0.05)*20,0)/20) END AS RRP
FROM #Temp
DEMO:http://rextester.com/QIM12944