In Sql Server, I have a simple table that store amount and balance like this:
ID | Date | Amount | Balance
-------------------------------------
101 | 1/15/2017 | 3.00 | 67.50
102 | 1/16/2017 | 5.00 | 72.50
103 | 1/19/2017 | 9.00 | 81.50
104 | 1/20/2017 | -2.00 | 79.50
If I changed a amount of a record, I need to update all the balance after that record.
ID | Date | Amount | Balance
-------------------------------------
101 | 1/15/2017 | 3.00 | 67.50
102 | 1/16/2017 | *5.02* | *72.52*
103 | 1/19/2017 | 9.00 | *81.52*
104 | 1/20/2017 | -2.00 | *79.52*
By now I have more than 100 million records in this table. To do this work, I don't want to use sql cursor or client program, it will submit plenty Update statements and take several hours to finish.
Is it can be done in one sql statement to re-calculate the balance of entire table?
You can easily do it in a single SQL statement using SUM() OVER.
eg
WITH tot as (select ID, SUM(Amount) as balance OVER (order by ID)
UPDATE Tab
SET Balance = t.Balance
FROM YOURTABLE tab
JOIN Tot
ON tot.id = tab.id
If the balance is reset by any other column then use this as a partition by clause and include in the join.
Now if you are inserting a new row you can simply run this update query with a where clause.
Related
I have an interesting conundrum and I am using SQL Server 2012 or SQL Server 2016 (T-SQL obviously). I have a list of products, each with their own UPC code. These products have a discontinue date and the UPC code gets recycled to a new product after the discontinue date. So let's say I have the following in the Item_UPCs table:
Item Key | Item Desc | UPC | UPC Discontinue Date
123456 | Shovel | 0009595959 | 2018-04-01
123456 | Shovel | 0007878787 | NULL
234567 | Rake | 0009595959 | NULL
As you can see, I have a UPC that gets recycled to a new product. Unfortunately, I don't have an effective date for the item UPC table, but I do in an items table for when an item was added to the system. But let's ignore that.
Here's what I want to do:
For every inventory record up to the discontinue date, show the unique UPC associated with that date. An inventory record consists of the "Inventory Date", the "Purchase Cost", the "Purchase Quantity", the "Item Description", and the "Item UPC".
Once the discontinue date is over with (e.g.: it's the next day), start showing only the UPC that is in effect.
Make sure that no duplicate data exists and the UPCs are truly being "attached" to each row per whatever the date is in the query.
Here is an example of the inventory details table:
Inv_Key | Trans_Date | Item_Key | Purch_Qty | Purch_Cost
123 | 2018-05-12 | 123456 | 12.00 | 24.00
108 | 2018-03-22 | 123456 | 8.00 | 16.00
167 | 2018-07-03 | 234567 | 12.00 | 12.00
An example query:
SELECT DISTINCT
s.SiteID
,id.Item_Key
,iu.Item_Desc
,iu.Item_Department
,iu.Item_Category
,iu.Item_Subcategory
,iu.UPC
,iu.UPC_Discontinue_Date
,id.Trans_Date
,id.Purch_Cost
,id.Purch_Qty
FROM Inventory_Details id
INNER JOIN Item_UPCs iu ON iu.Item_Key = id.Item_Key
INNER JOIN Sites s ON s.Site_Key = id.Site_Key
The real query I have is far too long to post here. It has three CTEs and the resultant query. This is simply a mockup. Here is an example result set:
Site_ID | Item_Key | Item_Desc | Item_Department | Item_Category | UPC | UPC_Discontinue Date | Trans_Date | Purch_Cost | Purch_Qty
2457 | 123456 | Shovel | Digging Tools | Shovels | 0009595959 | 2018-04-01 | 2018-03-22 | 16.00 | 8.00
2457 | 123456 | Shovel | Digging Tools | Shovels | 0007878787 | NULL | 2018-03-22 | 16.00 | 8.00
2457 | 234567 | Rakes | Garden Tools | Rakes | 0009595959 | NULL | 2018-07-03 | 12.00 | 12.00
2457 | 123456 | Shovel | Digging Tools | Shovels | 0007878787 | NULL | 2018-05-12 | 24.00 | 12.00
Do any of you know how I can "assign" a UPC to a specific range of dates in my query and then "assign" an updated UPC to the item for every effective date thereafter?
Many thanks!
Given your current Item_UPC table, you can generate effective start dates from the Discontinue Date using the LAG analytic function:
With Effective_UPCs as (
select [Item_Key]
, [Item_Desc]
, [UPC]
, coalesce(lag([UPC_Discontinue_Date])
over (partition by [Item_Key]
order by coalesce( [UPC_Discontinue_Date]
, datefromparts(9999,12,31))
),
lag([UPC_Discontinue_Date])
over (partition by [UPC]
order by coalesce( [UPC_Discontinue_Date]
, datefromparts(9999,12,31))
)) [UPC_Start_Date]
, [UPC_Discontinue_Date]
from Item_UPCs i
)
select * from Effective_UPCs;
Which yields the following Results:
| Item_Key | Item_Desc | UPC | UPC_Start_Date | UPC_Discontinue_Date |
|----------|-----------|------------|----------------|----------------------|
| 123456 | Shovel | 0007878787 | 2018-04-01 | (null) |
| 123456 | Shovel | 0009595959 | (null) | 2018-04-01 |
| 234567 | Rake | 0009595959 | 2018-04-01 | (null) |
This function produces a fully open ended interval where both the start and discontinue dates could be null indicating that it's effective for all time. To use this in your query simply reference the Effective_UPCs CTE in place of the Item_UPCs table and add a couple additional predicates to take the effective dates into consideration:
SELECT DISTINCT
s.SiteID
,id.Item_Key
,iu.Item_Desc
,iu.Item_Department
,iu.Item_Category
,iu.Item_Subcategory
,iu.UPC
,iu.UPC_Discontinue_Date
,id.Trans_Date
,id.Purch_Cost
,id.Purch_Qty
FROM Inventory_Details id
INNER JOIN Effective_UPCs iu
ON iu.Item_Key = id.Item_Key
and (iu.UPC_Start_Date is null or iu.UPC_Start_Date < id.Trans_Date)
and (iu.UPC_Discontinue_Date is null or id.Trans_Date <= iu.UPC_Discontinue_Date)
INNER JOIN Sites s ON s.Site_Key = id.Site_Key
Note that the above query uses a partially open range (UPC_Start_Date < trans_date <= UPC_Discontinue_Date instead of <= for both inequalities) this prevents transactions occurring exactly on the discontinue date from matching both the prior and next Item_Key record. If transactions that occur exactly on the discontinue date should match the new record and not the old simply swap the two inequalities:
and (iu.UPC_Start_Date is null or iu.UPC_Start_Date <= id.Trans_Date)
and (iu.UPC_Discontinue_Date is null or id.Trans_Date < iu.UPC_Discontinue_Date)
instead of
and (iu.UPC_Start_Date is null or iu.UPC_Start_Date < id.Trans_Date)
and (iu.UPC_Discontinue_Date is null or id.Trans_Date <= iu.UPC_Discontinue_Date)
I am trying to insert a running total column into a SQL Server table as part of a stored procedure. I am needing this for a financial database so I am dealing with accounts and departments. For example, let's say I have this data set:
Account | Dept | Date | Value | Running_Total
--------+--------+------------+----------+--------------
5000 | 40 | 2018-02-01 | 10 | 15
5000 | 40 | 2018-01-01 | 5 | 5
4000 | 40 | 2018-02-01 | 10 | 30
5000 | 30 | 2018-02-01 | 15 | 15
4000 | 40 | 2017-12-01 | 20 | 20
The Running_Total column provides a historical sum of dates less than or equal to each row's date value. However, the account and dept must match for this to be the case.
I was able to get close by using
SUM(Value) OVER (PARTITION BY Account, Dept, Date)
but it does not go back and get the previous months...
Any ideas? Thanks!
You are close. You need an order by:
Sum(Value) over (partition by Account, Dept order by Date)
Take an example I have the following transaction table, with transaction values of each department for each trimester.
TransactionID | Department | Trimester | Year | Value | Moving Avg
1 | Dep1 | T1 | 2014 | 13 |
2 | Dep1 | T1 | 2014 | 43 |
3 | Dep1 | T2 | 2014 | 36 |
300 | Dep1 T1 | 2017 | 28 |
301 | Dep2 T1 | 2014 | 24 |
I would like to calculate moving average for each transaction from the same department, taking the window as from the 6 trimesters to 2 trimesters before the current line's trimester. Example for transaction 300 in T1 2017, I'd like to have the average of transaction values for Dep1 from T1-2015 to T2-2016.
How can I achieve this with sliding window function in SQL Server 2014. My thought is that I should use something like
SELECT
AVG(VALUES) OVER
(PARTITION BY DEPARTMENT ORDER BY TRIMESTER,
YEAR RANGE [Take the range from previous 6 to 2 trimesters])
How would we define the RANGE clause. I suppose I could not use ROWS due to the number of rows for the window is unknown.
The same question for median. How would we rewrite for calculating the median instead of mean ?
I have two tables, first table section with schema as:
SecID | Date | SecReturn
-------|---------------|--------------
208 | 2015-04-01 | 0.00355
208 | 2015-04-02 | -0.00578
208 | 2015-04-03 | 0.00788
208 | 2015-04-04 | 0.08662
105 | 2015-04-01 | 0.00786
and the second table SectionDates with schema as:
SecID | MonthlyDate | DailyDate
------|---------------|-------------
208 | 2015-04-02 | 2015-04-03
105 | 2015-04-01 | 2015-04-01
I want to calculate the running product on SecReturn column of the table Section with date range (DailyDate to MonthlyDate) from second table SectionDates.
Running product will be calculated for each sectionID based on formula :
Date | SecReturn | SectionTotal
-----------|---------------|--------------------
2015-04-01 | X (lets say) | (1+x)-1
2015-04-01 | Y | (1+x)(1+y)-1
2015-04-01 | Z | (1+x)(1+y)(1+z)-1
After applying above calculation values will be computed in SectionTotal column as for date 2015-04-01 computed value will be (1+0.00355)-1. Similarly, for date 2015-04-02 computed value will be (1+0.00355)(1+-0.00578)-1 and for date 2015-04-03 computed value will be (1+0.00355)(1+-0.00578)(1+0.00788)-1 and so on.
The final output:
SecID | Date | SectionTotal
-------|------------|-----------------
105 | 2015-04-01 | 0.00786
208 | 2015-04-01 | 0.00355
208 | 2015-04-02 | -0.0022505
208 | 2015-04-03 | 0.0056117
You can try following query:
SELECT SecID, [Date], [SecReturn],
ROUND((1 + SecReturn) * COALESCE(v,1) - 1, 5) AS SectionTotal
FROM mytable AS t1
OUTER APPLY (
SELECT EXP(SUM(LOG(SecReturn + 1))) AS v
FROM mytable AS t2
WHERE t1.SecID = t2.SecID AND t1.[Date] > t2.[Date]) AS t3
OUTER APPLY, which is available in SQL Server 2005 AFAIK, fetches all records to be considered in running multiplication calculation.
Using the formula for a multiplication aggregate, found in this post you can obtain desired result.
Demo here
I have Database1 and Database2, i add each of them to a datatable object.
now Database1 is changed through Access, Records maybe added removed or row data might change.
so the database Looks like
+----+----------+-------+-------+
| ID | Name | Price | Sales |
+----+----------+-------+-------+
| 1 | ProductA | 453 | 55 |
| 2 | ProductB | 43 | 156 |
| 3 | ProductC | 22 | 161 |
+----+----------+-------+-------+
so if i Delete the row with Product ID=1 i want it to be deleted in the second Database2
if i add new product with ID = 4 i want it to be Added to Database2, also if i change Price or Sales i want it to change in Database2 for that record.
now the problem is the Database records are not in the same order in both databases so looping might take alot of time..for example if ID=4000 was removed than i've to loop through the whole Database to find this out.
so are thre anyother solutions than alot of loops?
It is possible to refer to another database in SQL run against an MS Access connection:
SELECT t1.*, t2.* FROM Table1 t1
INNER JOIN [;DATABASE=Z:\Docs\Test.accdb].Table1 t2
ON t1.ID=t2.ID
From here, it is easy enough to create queries to find missing or changed records.
If ID is the identifier for the row, then I do not think you can safely do what you are asking.
Given the example above for Table 1
+----+----------+-------+-------+
| ID | Name | Price | Sales |
+----+----------+-------+-------+
| 1 | ProductA | 453 | 55 |
| 2 | ProductB | 43 | 156 |
| 3 | ProductC | 22 | 161 |
+----+----------+-------+-------+
And simulating the out of order condition for Table 2
+----+----------+-------+-------+
| ID | Name | Price | Sales |
+----+----------+-------+-------+
| 1 | ProductB | 43 | 156 |
| 2 | ProductA | 453 | 55 |
| 3 | ProductC | 22 | 161 |
+----+----------+-------+-------+
If you delete ID 2 from table 2, you will end up deleting Product B from Table 1 (not product A). I don't think this is what you want.
Are you sure that ID is the primary key for these values?