I have a table like this:
I want RunningTotal column should be calculated as like:
Balance=Deposit+Balance and Withdraw-Balance on each row for example I want RunningTotal table must be like this:
AccountNo Deposit Withdraw RunningTotal
--------------- -------- -------- --------------
2014002 1000 0 1000
305002 0 500 500
50021 2500 100 2900
54201 6000 0 8900
Help me!
Here is one way using sum() over() works from 2012. Considering you have an id/date column to order the results
select AccountNo, Deposit, Withdraw,
RunningTotal = sum(Deposit-Withdraw)over(order by id Rows between UNBOUNDED PRECEDING and current row )
from Yourtable
For older versions
select AccountNo, Deposit, Withdraw,
cs.RunningTotal
from Yourtable a
cross apply(select sum(Deposit-Withdraw)
from Yourtable b
where a.id>=b.id) cs (RunningTotal)
Demo
Try a computed column in SQL Server, change the column RunningTotal to a computed column which calculates its value for each row based on Deposit - Withdraw. Like this:
CREATE TABLE #temp
(
AccountNo VARCHAR(50),
Deposit NUMERIC(19,2),
Withdraw NUMERIC(19,2),
RunningTotal AS (Deposit - Withdraw)
)
INSERT INTO #temp (AccountNo, Deposit, Withdraw)
SELECT '1234', 100, 50
SELECT * FROM #temp
Now the result will be like this
I'm adding 20 to the deposit and the RunningTotal will be automatically changed
UPDATE #temp
SET Deposit = Deposit + 20
SELECT * FROM #temp
If you have multiple rows for each account and want to calculate the RunningTotal for each row separately (assuming that you have a primary key/ identity column on your table - like below)
CREATE TABLE YourTable
(
SeqNo INT IDENTITY(1,1),
AccountNo VARCHAR(50),
Deposit NUMERIC(19,2),
Withdraw NUMERIC(19,2),
RunningTotal AS (Deposit - Withdraw)
)
Create an UDF to calculate the value, like this
CREATE FUNCTION dbo.fn_CalculateBalance
(#AccountNo VARCHAR(50), #Id INT)
RETURNS NUMERIC(19,2)
AS
BEGIN
DECLARE #RunningTotal NUMERIC(19,2)
SELECT #RunningTotal = SUM(Deposit) - SUM(Withdraw)
FROM YourTable
WHERE AccountNo = #AccountNo
AND SeqNo <= #Id
RETURN #RunningTotal
END
Now change the RunningTotal like this
CREATE TABLE YourTable
(
SeqNo INT IDENTITY(1,1),
AccountNo VARCHAR(50),
Deposit NUMERIC(19,2),
Withdraw NUMERIC(19,2),
RunningTotal AS (dbo.fn_CalculateBalance(AccountNo, SeqNo))
)
Or if you want to use the Date column instead on the SeqNo (identity column). Change the UDF to replace the check for SeqNo with the Date column.
When you look at the messages you can see the message "1 row(s) affected" multiple times. That's because you have this computed column and pass the date value in the computed column.
Related
I have 2 tables: Order and product_order. Every order has some product in it and that's because I store products another table.
Table Order:
Id name
Table PRODUCT_ORDER:
id product_id order_id
Before I start to insert, I don't know what the Order Id is. I want to insert the data into both tables at once and I need the order id to do that.
Both id's are auto incremented. I'm using SQL Server. I can insert first order and then find the id of the order and than execute the second insert, but I want to do these both to execute at once.
The output clause is your friend here.
DECLARE #Orders TABLE (OrderID INT IDENTITY, OrderDateUTC DATETIME, CustomerID INT)
DECLARE #OrderItems TABLE (OrderItemID INT IDENTITY, OrderID INT, ProductID INT, Quantity INT, Priority TINYINT)
We'll use these table variables as demo tables with IDs to insert into. You're liking going to be passing the set of items for an order in together, but for the purpose of a demo we'll ad hoc them as a VALUES list.
DECLARE #Output TABLE (OrderID INT)
INSERT INTO #Orders (OrderDateUTC, CustomerID)
OUTPUT INSERTED.OrderID INTO #Output
VALUES (GETUTCDATE(), 1)
We inserted the Order into the Orders table, and used the OUTPUT clause to cause the inserted (and generated by the engine) into the table variable #Output. We can now use this table however we'd like:
INSERT INTO #OrderItems (OrderID, ProductID, Quantity, Priority)
SELECT OrderID, ProductID, Quantity, Priority
FROM (VALUES (5,1,1),(2,1,2),(3,1,3)) AS x(ProductID, Quantity, Priority)
CROSS APPLY #Output
We cross applied it to our items list, and inseted it as if it was any other row.
DELETE FROM #Output
INSERT INTO #Orders (OrderDateUTC, CustomerID)
OUTPUT INSERTED.OrderID INTO #Output
VALUES (GETUTCDATE(), 1)
INSERT INTO #OrderItems (OrderID, ProductID, Quantity, Priority)
SELECT OrderID, ProductID, Quantity, Priority
FROM (VALUES (1,1,1)) AS x(ProductID, Quantity, Priority)
CROSS APPLY #Output
Just to demo a little farther here's another insert. (You likely wouldn't need the DELETE normally, but we're still using the same variable here)
Now when we select that data we can see the two separate orders, with their IDs and the products that belong to them:
SELECT *
FROM #Orders o
INNER JOIN #OrderItems oi
ON o.OrderID = oi.OrderID
OrderID OrderDateUTC CustomerID OrderItemID OrderID ProductID Quantity Priority
------------------------------------------------------------------------------------------------
1 2022-12-08 23:23:21.923 1 1 1 5 1 1
1 2022-12-08 23:23:21.923 1 2 1 2 1 2
1 2022-12-08 23:23:21.923 1 3 1 3 1 3
2 2022-12-08 23:23:21.927 1 4 2 1 1 1
Dale is correct. You cannot insert into multiple tables at once, but if you use a stored procedure to handle your inserts, you can capture the ID and use it in the next insert.
-- table definitions
create table [order]([id] int identity, [name] nvarchar(100))
go
create table [product_order]([id] int identity, [product_id] nvarchar(100), [order_id] int)
go
-- stored procedure to handle inserts
create procedure InsertProductWithOrder(
#OrderName nvarchar(100),
#ProductID nvarchar(100))
as
begin
declare #orderID int
insert into [order] ([name]) values(#OrderName)
select #orderID = ##identity
insert into [product_order]([product_id], [order_id]) values(#ProductID, #orderID)
end
go
-- insert records using the stored procedure
exec InsertProductWithOrder 'Order ONE', 'AAAAA'
exec InsertProductWithOrder 'Order TWO', 'BBBBB'
-- verify the results
select * from [order]
select * from [product_order]
I am not able to use the recently inserted Quantity value of #ACCT table in the Select statement to be used like
ser.ServiceRate * Quantity
Every time I get the ERROR
Cannot insert the value NULL into column 'Amount'
I am just a beginner so any pointers to solve this would help.
DECLARE #MinReservationId INT = (SELECT MIN(f.ReservationId) FROM dbo.Reservation AS f)
DECLARE #MaxReservationId INT = (SELECT MAX(f.ReservationId) FROM dbo.Reservation AS f)
DECLARE #QuantityNew INT
WHILE #MinReservationId <= #MaxReservationId
BEGIN
CREATE TABLE #Acct
(
ServiceId INT,
Quantity INT --> I WANT THIS VALUE TO BE USED BELOW
)
INSERT INTO dbo.[Transaction]
(
ReservationId,
ServiceId,
Rate,
Quantity,
Amount
)
OUTPUT inserted.ServiceId,Inserted.Quantity
INTO #Acct
(
ServiceId,
Quantity
)
SELECT
#MinReservationId,
ser.ServiceId,
ser.ServiceRate,
ABS(CHECKSUM(NEWID())%3) + 1,
ser.ServiceRate * (SELECT acc.Quantity from #Acct as acc) -> QUANTITY from #ACCT should be used here
FROM dbo.[Service] AS ser
SELECT #MinReservationId=#MinReservationId+1
Drop table #Acct
END
You can use a CTE in order to capture the NEWID() value created for every record of table dbo.[Service]. Then use this CTE to do the INSERT:
;WITH ToInsert AS
(
SELECT ServiceId ,
ServiceRate,
ABS(CHECKSUM(NEWID())%3) + 1 AS Quantity
FROM dbo.[Service]
)
INSERT INTO dbo.[Transaction]
(
ReservationId,
ServiceId,
Rate,
Quantity,
Amount
)
SELECT #MinReservationId,
ServiceId,
ServiceRate,
Quantity,
ServiceRate * Quantity
FROM ToInsert
I'm trying to create a report with one row per customer, however there are multiple rows per customer.
The current view is:
Customer Business Dept Type Status
-----------------------------------------------
019 Public null null null
019 null IT null null
019 null null Retail 0 --char(1)
My desired view is:
Customer Business Dept Type Status
-----------------------------------------------
019 Public IT Retail 0
I'm using SQL Server 2008 R2. There are more columns in my data set, but this is a sample. I'm unsure of how to achieve the results when my datatype is character and not INT based.
If this is a representative example, and each column will always have a single row with a value and the others will have nulls, you could use an aggregate max or min, which ignore nulls:
SELECT customer, MAX(business), MAX(dept), MAX(type), MAX(status)
FROM mytable
GROUP BY customer
try something this:
CREATE TABLE #tmp ([Customer] CHAR(3), [Business] VARCHAR(20), [Dept] VARCHAR(20), [Type] VARCHAR(20), [Status] CHAR(1))
INSERT INTO #tmp (Customer, Business) VALUES ( '019', 'Public')
INSERT INTO #tmp (Customer,Dept) VALUES ('019','IT')
INSERT INTO #tmp (Customer,[Type]) VALUES ('019','Retail')
INSERT INTO #tmp (Customer,[Status]) VALUES ('019','0')
SELECT * FROM #tmp AS t
SELECT t.Customer, t.Business, t2.Dept, t3.[Type], t4.[Status] FROM #tmp AS t
JOIN #tmp AS t2 ON t2.Customer = t.Customer
JOIN #tmp AS t3 ON t3.Customer = t.Customer
JOIN #tmp AS t4 ON t4.Customer = t.Customer
WHERE t.Business IS NOT NULL AND t2.Dept IS NOT NULL AND t3.[Type] IS NOT NULL AND t4.[Status] IS NOT NULL
i have a table called table1 and it has following columns.
suppose there are records like localamount is 20,000, 30000,50000, 100000 then as per my condition i have to delete records from this table according to the group by set of site id, till id, transid,shift id where localamount exceeds 10,000... the rest of the records can be available?
my aim is to delete rows from this table where local amount is exceeds 10,0000 according to site id, till id, transid,shift id
SiteId varchar(10),
TillId tinyint,
ShiftId int,
TransId int,
TranDate datetime,
SettlementType varchar(5),
CreditCardNumber varchar(25),
ProductTypeCode varchar(10),
NewProductTypeCode varchar(10),
TransactionType int,
ForeignAmount money,
LocalAmount money,
ProductCode varchar(10)
Im not sure I understand what you are saying, but couldn't you do this without a group by?
delete from table1 where LocalAmount > 10,0000[sic] and SiteId = whatever and TillId = whatever...
obviously take the [sic] out...
Assuming you want to delete the whole group where the sum is > 10000
;with cte as
(
select sum(localamount) over
(partition by siteid, tillid, transid,shiftid) as l,
* from table1
)
delete from cte where l>10000
I have an sql scenario as follows which I have been trying to improve.
There is a table 'Returns' which is having ids of the returned goods against a shop for an item. Its structure is as below.
Returns
-------------------------
Return ID | Shop | Item
-------------------------
1 Shop1 Item1
2 Shop1 Item1
3 Shop1 Item1
4 Shop1 Item1
5 Shop1 Item1
There is one more table Supplier with Shop, supplier and Item as shown below.
Supplier
---------------------------------
Supplier | Shop | Item | Volume
---------------------------------
supp1 Shop1 Item1 20%
supp2 Shop1 Item1 80%
Now as you see supp1 is supplying 20 % of total item1 volume and supp2 is supplying 80% of Item1 to shop1. And there were 5 return of items against the same Item1 for same Shop1.
Now I need to allocate any four return IDs to Supp1 and remaining one return Id to supp2. This allocation of numbers is based on the ratio of the supplied volume percentage of the supplier. This allocation varies depending on the ratio of volume of supplied items.
Now I have tried a method of using RANKs as shown below by use of temp tables.
temp table 1 will have Shop, Return Id, Item, Total count of return IDs and Rank of the return id.
temp table 2 will have shop, Supplier, Item and his proportion and rank of proportion.
Now I am facing the difficulty in allocating top return ids to top supplier as illustrated above. As SQL doesnt have loops how can I achieve this. I have been tying several ways of doing this.
My environment is Teradata (ANSI SQL is enough).
UPDATE:
You need to loop, here is some SQL code you can use as a starting point.
Basically I use temporary tabes and ROW_NUMBER().
For my sample I used SQL SERVER 2008.
Try the following:
-- gather suppliers in temp table
DECLARE #SupplierTemp table
( [RowId] int
,[Supplier] nvarchar (50)
,[ReturnCount] int )
-- gather supplier with return count
INSERT INTO #SupplierTemp
SELECT ROW_NUMBER() OVER(ORDER BY [Supplier].[Supplier] DESC, [Supplier].[Supplier])
,[Supplier].[Supplier]
, COUNT([Supplier].[Supplier])*[Supplier].[Volume]/100 AS ReturnCount
FROM [Supplier]
INNER JOIN [Returns] ON (([Returns].[Item] = [Supplier].[Item])
AND ([Returns].[Shop] = [Supplier].[Shop]))
GROUP BY [Supplier].[Supplier], [Supplier].[Volume]
ORDER BY [Supplier].[Supplier]
-- gather returns in temp table
DECLARE #ReturnsTemp table
( [RowId] int
,[Id] int)
-- gather returns
INSERT INTO #ReturnsTemp
SELECT ROW_NUMBER() OVER(ORDER BY [Returns].[Id] DESC, [Returns].[Id])
,[Returns].[Id]
FROM [Returns]
-- gather results in temp table
DECLARE #ResultsTemp table
( [Supplier] nvarchar(50)
,[Id] int)
DECLARE #rrowid as int
DECLARE #rid as int
-- loop over all suppliers
-- loop once for each [ReturnCount]
-- find the next avialable Id
DECLARE #srowid as int
DECLARE #loopCnt as int
DECLARE #supplier as nvarchar(50)
-- get first supplier
SELECT #srowid = (SELECT MIN([RowId]) FROM #SupplierTemp)
SELECT #loopCnt = [ReturnCount] FROM #SupplierTemp WHERE [RowId] = #srowid
SELECT #supplier = [Supplier] FROM #SupplierTemp WHERE [RowId] = #srowid
-- loop over suppliers
WHILE #srowid IS NOT NULL
BEGIN
-- loop of number of returns
WHILE #loopCnt > 0
BEGIN
-- find the Id to return
SELECT #rrowid = (SELECT MIN([RowId]) FROM #ReturnsTemp)
SELECT #rid = [Id] FROM #ReturnsTemp WHERE [RowId] = #rrowid
INSERT INTO #ResultsTemp VALUES (#supplier, #rid)
DELETE FROM #ReturnsTemp WHERE [RowId] = #rrowid
SELECT #loopCnt = #loopCnt - 1
END
-- delete current item from table to keep loop moving forward...
DELETE FROM #SupplierTemp WHERE [RowId] = #srowid
-- get next supplier.
SELECT #srowid = (SELECT MIN([RowId]) FROM #SupplierTemp)
SELECT #loopCnt = [ReturnCount] FROM #SupplierTemp WHERE [RowId] = #srowid
SELECT #supplier = [Supplier] FROM #SupplierTemp WHERE [RowId] = #srowid
END
SELECT * FROM #ResultsTemp