SQL decrement a value based on two columns till 0 - sql

I have the following datasets (just a sample):
Table1:
ID MAX AMT SORTED
1 20 0 1
1 30 0 2
1 40 0 3
1 50 0 4
2 0 0 1
2 30 0 2
2 40 0 3
2 40 0 4
...
Table2:
ID AMT
1 75
2 70
...
I must update Table1.AMT from Table2.AMT using this rules:
Table1 and Table2 are joined on ID
Table1.AMT can't hold larger value than MAX
if Table2.AMT >= Table1.MAX then Table1.AMT = Table1.MAX... then on the next row update Table1.AMT with Table2.AMT - previous record AMT still using the above rules.
So the expected output would be
ID MAX AMT SORTED
1 20 20 1
1 30 30 2
1 40 25 3
1 50 0 4
2 0 0 1
2 30 30 2
2 40 40 3
2 40 0 4
...
How can one achieve that?
I thought of creating a temp table with an aggregated SUM() of Table1.MAX, and using that as a reference to update Table1.AMT (if SUM(MAX) < Table2.AMT then Table1.AMT = Table1.MAX else Table1.AMT = previous records SUM(MAX)).
But can it be done without a temp table? (Sadly I can't create functions and procedures in my work env.)

More efficient solution can be made using specifics or Oracle PL/SQL.
Here is a generic solution:
select t1.ID, min(t1.MAX) as MAX, least(min(t1.MAX),coalesce(min(t2.AMT),0)-coalesce(least(sum(t1p.MAX-t1p.AMT), min(t2.AMT)),0)+min(t1.AMT)) as AMT, t1.SORTED
from Table1 t1
left join Table2 t2 on t2.ID = t1.ID
left join Table1 t1p on t1p.ID = t1.ID and t1p.SORTED < t1.SORTED
group by t1.ID, t1.SORTED
order by t1.ID, t1.SORTED
explanation of calculating AMT:
AMT is smallest of "MAX for the row" and "How much is possible"
least(min(t1.MAX),"How much is possible")
"How much is possible": max available - how much was given for previous rows + how much we already have
coalesce(min(t2.AMT),0) - "how much was given for previous rows" + min(t1.AMT)
"how much was given for previous rows": smalles of how much required to fill and how much possible
coalesce(least(sum(t1p.MAX-t1p.AMT), min(t2.AMT)),0)

Related

eliminating all the null values and showing all the values in one record

I need to make a select statement to table1 so that it looks like table2 oracle. Please help. I tried some case when and pivot but I failed again and again.
table1:
Product abc def ghi
1 100 0 0
1 0 10 0
1 0 0 20
2 0 0 80
2 0 60 0
2 3 0 0
table2:
product abc def ghi
1 100 10 20
2 3 60 80
Do aggregation :
select product, max(abc), max(def), max(ghi)
from table1 t1
group by product;
However, the value you have provided with sample data the sum() would also work. If, table has NULL instead of 0 then both function would work but i would prefer to use max() if the null values in table.

Using SQL to get the continuous range of table

Here is a table named track and have three columns: time, speed, isOverSpeed, like below
TIME Speed IsOverSpeed
10:00:01 76 1
10:00:11 58 0
10:00:21 70 1
10:00:31 70 1
10:00:41 90 1
10:00:51 40 0
10:01:01 30 0
10:01:11 80 1
10:01:21 20 0
What I want is to calculate how many times and how long a car which is overspeed, the requirement is the continuous overspeed records will be count as 1 time, in the example, there is 3 times overspeed not 4. I am thinking using the window function but seems it does not work. could anyone has an idea?
PS. the result should be 3 times, 50 seconds.
I think you need something like this:
SELECT
COUNT(*) As Cnt, SUM(SECONDS_BETWEEN(dt.prevTime, dt.TIME)) As someTime
FROM (
SELECT t1.TIME, MAX(t2.TIME) As prevTime
FROM aTable t1
JOIN
aTable t2 ON t1.TIME > t2.TIME
WHERE t1.IsOverSpeed = 1
GROUP BY t1.TIME) dt

Update row based on value of multiple other rows in Oracle SQL

I want to find the rows which are similar to each other, and update a field if a row has any similar row. My table looks like this:
OrderID | Price | Minimum Number | Maximum Number | Volume | Similar
1 45 2 10 250 0
2 46 2 10 250 0
3 60 2 10 250 0
"Similar" in this context means that the rows that have same Maximum Number, Minimum Number, and Volume. Prices can be different, but the difference can be at most 2.
In this example, orders with OrderID of 1 and 2 are similar, but 3 has no similar row (since even if it has same Minimum Number, Maximum Number, and Volume, but its price is not within 2 units from orders 1 and 2).
Then, I want to update the filed "Similar" for orders 1 and 2 from the default value (0) to 1. So, the output for the example above would be:
OrderID | Price | Minimum Number | Maximum Number | Volume | Similar
1 45 2 10 250 1
2 46 2 10 250 1
3 60 2 10 250 0
Here is one method that is ANSI standard SQL that will work in most databases, including Oracle. It implements the logic that you set out using a correlated subquery:
update table t
set similar = 1
where exists (select 1
from table t2
where t2.minimum = t.minimum and
t2.maximum = t.maximum and
t2.volume = t.volume and
abs(t2.price - t.price) <= 2 and
t2.OrderId <> t.OrderId
);
EDIT:
It occurs to me that the "similar" field might be the minimum OrderId of the similar fields. You can extend the above idea to:
update table t
set similar = (select min(orderId)
from table t2
where t2.minimum = t.minimum and
t2.maximum = t.maximum and
t2.volume = t.volume and
abs(t2.price - t.price) <= 2 and
t2.OrderId <> t.OrderId
)
where exists (select 1
from table t2
where t2.minimum = t.minimum and
t2.maximum = t.maximum and
t2.volume = t.volume and
abs(t2.price - t.price) <= 2 and
t2.OrderId <> t.OrderId
);
Although if this were the case, the default value should be NULL and not 0.

Select Previous Record in SQL Server 2008

Here's the case: I have one table myTable which contains 3 columns:
ID int, identity
Group varchar(2), not null
value decimal(18,0), not null
Table looks like this:
ID GROUP VALUE Prev_Value Result
------------------------------------------
1 A 20 0 20
2 A 30 20 10
3 A 35 30 5
4 B 100 0 100
5 B 150 100 50
6 B 300 200 100
7 C 40 0 40
8 C 60 40 20
9 A 50 35 15
10 A 70 50 20
Prev_Value and Result columns should be custom columns. I need to make it on view. Anyone can help? please... Thank you so much.
The gist of what you need to do here is to join the table to itself, where part of the join condition is that the value column of the joined copy of the table is less than value column of the original. Then you can group by the columns from the original table and select the max value from the joined table to get your results:
SELECT t1.id, t1.[Group], t1.Value
, coalesce(MAX(t2.Value),0) As Prev_Value
, t1.Value - coalesce(MAX(t2.Value),0) As Result
FROM MyTable t1
LEFT JOIN MyTable t2 ON t2.[Group] = t1.[Group] and t2.Value < t1.Value
GROUP BY t1.id, t1.[Group], t1.Value
Once you can update to Sql Server 2012 you'll also be able to take advantage of the new LAG keyword.

Inserting a new indicator column to tell if a given row maximizes another column in SQL

I currently have a table in SQL that looks like this
PRODUCT_ID_1 PRODUCT_ID_2 SCORE
1 2 10
1 3 100
1 10 3000
2 10 10
3 35 100
3 2 1001
That is, PRODUCT_ID_1,PRODUCT_ID_2 is a primary key for this table.
What I would like to do is use this table to add in a row to tell whether or not the current row is the one that maximizes SCORE for a value of PRODUCT_ID_1.
In other words, what I would like to get is the following table:
PRODUCT_ID_1 PRODUCT_ID_2 SCORE IS_MAX_SCORE_FOR_ID_1
1 2 10 0
1 3 100 0
1 10 3000 1
2 10 10 1
3 35 100 0
3 2 1001 1
I am wondering how I can compute the IS_MAX_SCORE_FOR_ID_1 column and insert it into the table without having to create a new table.
You can try like this...
Select PRODUCT_ID_1, PRODUCT_ID_2 ,SCORE,
(Case when b.Score=
(Select Max(a.Score) from TableName a where a.PRODUCT_ID_1=b. PRODUCT_ID_1)
then 1 else 0 End) as IS_MAX_SCORE_FOR_ID_1
from TableName b
You can use a window function for this:
select product_id_1,
product_id_2,
score,
case
when score = max(score) over (partition by product_id_1) then 1
else 0
end as is_max_score_for_id_1
from the_table
order by product_id_1;
(The above is ANSI SQL and should run on any modern DBMS)