deleting values with overlapping dates - sql

I have table in an MS Access database that looks like this:
ID Symbol Direction Start_val End_val AW
1 ABC up 100 120 10
2 ABC up 110 130 11
3 XYZ down 350 380 15
4 XYZ down 340 390 15
I am trying to delete duplicate symbols and directions that have overlapping start_val and end_val and the highest AW. For example in the table above the data matching id 1 has a start_val and end_val that overlap the start_val and end_val of id 2. Since id 1 has a smaller AW i want to delete that. For id 3 and 4, the start_val and end_val overlap but the AW is the same, so the smallest id is deleted.

This should do the trick:
delete tablename
from tablename t1
inner join tablename t2 on t1.Symbol = t2.Symbol
and t1.Direction = t2.Direction
and t1.Start_val >= t2.Start_val
and t1.End_val <= t2.End_Val
and t1.AW <= t2.AW
Making the inner join with the same table with the restrictions:
equal Symbol;
equal Direction;
Bigger (or equal) Start_val;
Lesser (or equal) End_val;
Lesser (or equal) AW.
will get you the list of rows that you want to delete.

Related

how can i get an accurate count based on max date when joining 3 tables when one of the join fields is many to 1 in oracle?

So, I have 3 tables that I am attempting to get counts for based on a groupid, and a task code. There are a few issues I am having as some of the relationships are many to one, which I think is somehow inflating my counts. I will list my 3 tables with the pertinent attributes.
task_table contains:
task_code - would like to get the counts of each one in a group id, would like to use the latest instance basedon event date.
sol_id -used to join to worktable; many sol_id to one m_id is possible
edate -need to use to get one record
cur_id - where cur_id = 1 in the where clause
worktable contains:
sol_id - used to join to task_table
m_id - used to join to grouptable
grouptable contains:
m_id
groupid- used to group the task_code to get count
I'd like the end result to look like:
group_id task_count task
5555 45 A
5555 4 N
5624 67 A
5624 23 O
5624 42 X
I have been attempting to run a number of queries, but the counts I am getting back do not look correct. I am concerned that it is somehow returning more than one instance of the m_id somehow? Here is the query in question:
select c.groupid, count(c.groupid) group_count, a.task_code from task_table a
join worktable b
on a.sol_id = b.sol_id
join grouptable c
on b.m_id= c.m_id
where a.cur_id = 1 and a.task_code is not null
group by c.groupid, a.task_code;
If I add 'edate = (select max(edate) from task_table)' in the where clause, it returns an empty table.
I am unsure how to incorporate edate to get only the newest record that fits the criteria in the where clause. The reason I think I want to use this is because there could be more than one sol_id that is associated with a m_id, so i'd just like to include only the newest record with a cur_id in the count. Thank you for your time.
sample data
task_table
task_code sol_id edate cur_id
A 23 6/7/09 1
A 24 6/4/09 1
A 23 6/10/09 0
B 45 6/2/09 1
B 42 6/3/09 1
C 34 10/8/10 0
C 83 9/10/09 1
work table
sol_id m_id
23 1234
24 1234
45 1832
42 1343
83 7623
group table
m_id group_id
1234 A76
1832 Y23
1343 A76
7623 Y23
looking at these tables, the result should look like the following
group_id task_count task
A76 2 A
Y23 1 C
( A76 should only count sol_id 23 and 42)
( Y23 should only count sol_id 83)
So, there's a conflict in your requested data result. According to your own sample, A76 should have a task_count of 2: sol_id 23, which has Task A, and sol_id 42, which has Task B. It's not possible to have it return a row like you have at your example result table because it would need to group by TASK_CODE, which means losing the COUNT(task_code). Can't have it both ways.
In order to obtain only the most recent edate, I did a separate calculation to location that max(edate) by task_code, then joined it back to obtain the sol_id. If this isn't accurate for your data set, you'll need to determine another way of obtaining max(edate). This works for your sample set.
with recentTasks as (
select task_code, max(edate) as recentDate
from task_table m
where cur_id = 1
and task_code is not null
group by task_code
), recentTaskWithSols as (
select m.task_code, m.recentDate as edate, t.sol_id
from recentTasks m
join task_table t on m.task_code = t.task_code AND m.recentDate = t.edate
where t.cur_id = 1
)
select c.group_id,
count(a.sol_id) task_count
from group_table c
join work_table b on c.m_id = b.m_id
join recentTaskWithSols a on b.sol_id = a.sol_id
group by c.group_id;
gives the result:
+------------------------+
| GROUP_ID | TASK_COUNT |
+------------------------+
| A76 | 2 |
| Y23 | 1 |
+-----------+------------+
Demo here.

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.

Create duplicate records in a query for MS Access

I have the following table in a Microsoft Access Database:
TableName: Cabinets
RoomID - Number
Wall ID - Number
Cabinet ID - Number
Width - Number (double)
Height - Number (double)
Depth - Number (double)
Quantity - Number
What I need to do is create a query that will duplicate each row for a number of times specified in the Quantity field. As an example, let's say that I have the following data:
Room ID Wall ID Cabinet ID Width Height Depth Quantity
1 1 1 30 34.5 24 1
1 1 2 42 34.5 24 1
1 1 3 18 34.5 24 2
I need to have a query that would create the following:
Room ID Wall ID Cabinet ID Width Height Depth
1 1 1 30 34.5 24
1 1 2 42 34.5 24
1 1 3 18 34.5 24
1 1 3 18 34.5 24
Now, I have seen, in other questions, that I can create a 'numbers' table to accomplish this, unfortunately, I can't change the table at all. In fact, I am very limited to what I can actually do with this database.
Here is what I can do:
Create a Query that will pull the data
Create a Query that will add a 'view' to the database at runtime (before the query to pull the data is run)
Any help that can be given would be greatly appreciated. Thank you very much in advanced.
Well, this is incredibly painful in Access, but you can create a numbers table on the fly. Let's assume that cabinet_id is really a unique id in the cabinets table.
select c.*
from cabinets c left join
(select (select count(*) from cabinets c2 where c2.cabinet_id <= c.cabinet_id) as n
from cabinets c
) n
on n.n <= c.quantity;
This uses the cabinets table to generate a list of numbers, using a correlated subquery to get the numbers. Note that this assumes that quantity is always less than the number of rows in this table.
If you know the ids have no gaps and start at 1, you can simplify this to:
select c.*
from cabinets c left join
(select cabinet_id) as n
from cabinets c
) n
on n.n <= c.quantity;

SQL decrement a value based on two columns till 0

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)