I have a table of inventory transactions. I need to select the dates of the last few transactions, up until the adjusted quantity is greater then the current amount on hand in inventory.
I am dealing with three columns: item, transaction_date, adj_qty. Each item will have multiple transaction dates and adjustment quantities.
How do I return the all the transaction_dates for each item until the item reached a certain threshold (i.e. accumulate 100). Say the first item has 2000 transactions and the last five transactions have each a qty of 21. I would like the query to return transaction the last 5 because that is when the item reached 100.
If possible I'd like to do this without a loop or cursor.
Can anybody help?
What you need is a cumulative sum. This is built into SQL Server 2012.
Alas, with that, you need to do it with a self join:
select t.item, t.transaction_date, t.adj_qty,
sum(tprev.adj_qty) as CumSum
from t t join
t tprev
on t.item = tprev.item and
t.transaction_date >= tprev.transaction_date
group by t.item, t.transaction_date, t.adj_qty
having 100 between sum(tprev.adj_qty) -t.adj_qty + 1 and sum(tprev.adj_qty)
Notice the use of the self join and group by to do the cumulative sum. Not pleasant, but necessary without the order clause in the SUM() over (partition by) function. This cumulative sum adds everything up from the first record (by transaction date) for an item up to any other.
The HAVING clause then selects the row you are looking for, where the cumulative sum has increased passed some threshhold.
Related
I am trying to do a count by month of the total number of items (serialnumber) that appears in inventory.
This usually can be easily solved with distinct, however, I only want to count if it is the first occurrence that it appears (first insert).
This query gets me most of the way there.
select date_trunc (‘month’,date) as Date,productid, count(distinct serialnumber) from inventory
where date_trunc(‘month’,date)>= ‘2016-01-01’ and productID in ('1','2') and status = ‘INSERT’
group by date_trunc(‘month’,date), productid
order by date_trunc(‘month’,date) desc
But I realize I am double/triple/quadruple counting some serial numbers because an item can reappear in our inventory multiple times over the course of its lifecycle.
The query above covers these scenarios since the serial numbers appear once:
Shows up as new
Shows up as used
Below are the use cases where I realize I may be double/triple/quadruple counting:
Shows up as new then comes back around as used (no limit to how many times it can appear used)
Shows up used then comes back again as used (no limit to how many times it can appear used)
Here's an example I ran into.
(Note: I have added the condition column to better illustrate this). But the particular serial number has been in inventory three times (first as new, then as used twice)
Date
ProductID
Count
Condition
7-1-21
1
1
u
11-1-18
1
1
u
2-1-17
1
1
n
In my current query results, each insert gets counted (once in Feb 2017, once in Nov 2018 and once in July 2021).
How can I amend my query to make sure I'm only counting the very first instance (insert) a particular serial number appears in the inventory table?
In the subquery calculate first insert date only of each product/item using min aggregate function. Then count the items on that result:
select Date, productid, count(serialnumber)
from (
select min(date_trunc(‘month’,date)) as Date, productid, serialnumber
from inventory
where date_trunc(‘month’,date) >= ‘2016-01-01’
and productID in ('1','2')
and status = ‘INSERT’
group by productid, serialnumber
) x
group by Date, productid
order by Date desc;
I made really simple example table with columns date and credit, so we can sum all credit to get account saldo of the account. I can sum all credit values to get saldo, but that is not what I want. I want to calculate average saldo, so in order to do that I need to use RangeDate table with date of every day and query with this logic:
SELECT DRA.date, SUM(ACB.credit)
FROM AccountBalance ACB
JOIN DateRange DRA ON ACB.date <= DRA.date
GROUP BY DRA.date
http://sqlfiddle.com/#!18/88afa/10
the problem is when using this program on a larger range of dates, like whole year for example.
Instruction is telling SQL Engine to sum up all rows of credit before the current date including credit of the current row date (where the cursor is in that moment (JOIN... ACB.date <= DRA.date)), in order to get accounts credit for that day.
This is inefficient and slow for big tables because that sum already exists in the row 1 level before, and I would like to tell SQL Engine to take that sum and only add the one row of credit that it is in.
Somone told me that I should use LAG function, but i need an example first.
I think you simply need an analyitc function -
SELECT DRA.date,
SUM(ACB.saldo) OVER (ORDER BY DRA.date)
FROM DateRange DRA
LEFT JOIN AccountBalance ACB ON ACB.date = DRA.date;
Demo.
I am try to calculate the average since the last time stamp and pull all records where the average is greater than 3. My current query is:
SELECT AVG(BID)/BID AS Multiple
FROM cdsData
where Multiple > 3
and SqlUnixTime > 1492225582
group by ID_BB_RT;
I have a table cdsData and the unix time is april 15th converted. Finally I want the group by calculated within the ID as I show. I'm not sure why it's failing but it says that the field Multiple is unknown in the where clause.
I am try to calculate the average since the last time stamp and pull all records where the average is greater than 3.
I think your intention is correctly stated as follows, "I am trying to calculate the average since the last time stamp and select all rows where the average is greater than 3 times the individual bid".
In fact, a still better restatement of your objective would be, "I want to select all rows since the last time stamp, where the bid is less than 1/3rd the average bid".
For this, the steps are as follows:
1) A sub-query finds the average bid divided by 3, of rows since the last time stamp.
2) The outer query selects rows since the last time stamp, where the individual bid is < the value returned by the sub-query.
The following SQL statement does that:
SELECT BID
FROM cdsData
WHERE SqlUnixTime > 1492225582
AND BID <
(
SELECT AVG(BID) / 3
FROM cdsData
WHERE SqlUnixTime > 1492225582
)
ORDER BY BID;
1)
SQL is evaluated backwards, from right to left. So the where clause is parsed and evaluate prior to the select clause. Because of this the aliasing of AVG(BID)/BID to Multiple has not yet occurred.
You can try this.
SELECT AVG(BID)/BID AS Multiple
FROM cdsData
WHERE SqlUnixTime > 1492225582
GROUP BY ID_BB_RT Having (AVG(BID)/BID)>3 ;
Or
Select Multiple
From (SELECT AVG(BID)/BID AS Multiple
FROM cdsData
Where SqlUnixTime > 1492225582 group by ID_BB_R)X
Where multiple >3
2)
Once you corrected the above error, you will be having one more error:
Column 'BID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
To correct this you have to insert BID column in group by clause.
I would like to add a discount rate when summing Cashflows over a number of period. To do this I need to multiply each of the remaining cashflows by the discount rate, consummate with this period. I could do this, if I knew the row number of each period, but I can't use it with the window calc I am using. The example below shows the column 'Remaining Interest' which is what I am trying to calculate based on raw data of period and interest.
select Period,RemainingInterest = SUM(PeriodInterestPaid)
OVER (PARTITION BY Name ORDER BY period ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
FROM CF A
Period Interest Remaining Interest(Query) Remaining Interest(Required)
1 1000 1000+2000 1000/1.02^1+2000/1.02^2
2 2000 2000 2000/1.02^1
hi i hope i understand Well ---
you need to get the sum of value based on the period that what i under stand from the query but u said that you need a multiply
So there's no need to make a window function just group by
select Period, SUM(PeriodInterestPaid) as RemainingInterest
FROM CF A
and if u want a multiplay you will make group by also but u will use anther exp :
Pls explan what exactly u need
My source data includes Transaction ID, Date, Amount. I need a one week trailing average which moves on a daily basis and averaging amount per transaction. Problem is, that sometimes there is no transactions in particuliar date, and I need avg per transaction, no per day, and trailing average moves by day, not by week.In this particular case I can't use OVER with rows preceding. I'm stack with it :(
Data looks like this:
https://gist.github.com/avitominoz/a252e9f1ab3b1d02aa700252839428dd
There are two methods to doing this. One uses generate_series() to get all the results. The second uses a lateral join.
with minmax as (
select min(trade_date) as mintd, max(trade_date) as maxtd
from sales
)
select days.dte, s.values,
avg(values) over (order by days.dte
rows between 6 preceding and current row
) as avg_7day
from generate_series(mintd, maxtd, interval '1 day') days(dte) left join
sales s
on s.trade_dte = days.dte;
Note: this ignores the values on missing days rather than treating them as 0. If you want 0, then use avg(coalesce(values, 0)).