Oracle update query using partition - sql

I have following sample data in Oracle v.12 database
ID | NAME | DML_TYPE | FND_FILESEQNO | FND_FILERBA
---------------------------------------------------------
1 | name1a | insert | 1 | 1
1 | name1b | update | 1 | 2
2 | name2a | insert | 2 | 1
2 | name2b | update | 2 | 2
....
....
....
I want following 2 transactions to happen
delete old records (FND_FILESEQNO + FND_FILERBA) partition by 'ID' column
update latest record DML_TYPE = 'insert'
So eventually, if I query this table, I should get following result...
ID | NAME | DML_TYPE | FND_FILESEQNO | FND_FILERBA
---------------------------------------------------------
1 | name1b | insert | 1 | 2
2 | name2b | insert | 2 | 2
Many thanks

Try This:-
MERGE INTO STACTOVER a
USING ( SELECT * FROM (
SELECT STACTOVER.*,Row_Number() OVER(PARTITION BY ID ORDER BY ID)rn
FROM STACTOVER)WHERE rn>1
)b
ON
(a.ID = b.ID)
WHEN MATCHED THEN
UPDATE SET a.dml_type = 'insert'
DELETE WHERE a.NAME != b.NAME ;

Related

How do I do an Oracle SQL update from select in a specific order?

I have a table with old values (some null) and new values for various attributes, all inserted at different add times throughout the months. I'm trying to update a second table with records with business month end dates. Right now, these records only contain the most recent new values for all month end dates. The goal is to create historical data by updating the previous month end values with the old values from the first table. I am a beginner and was able to come up with a query to update on one object where there was one entry from the first table. Now I am trying to expand the query to include multiple objects, with possible, multiple old values within the same month. I tried to use "order by" (since I need to make updates for a month in ascending order so it gets the latest value) but read that doesn't work with update statements without a subquery. So I tried my hand at making a more complicated query, without success. I am getting the following error: single-row subquery returns more than one row. Thanks!
TableA:
| ID | TYPE | OLD_VALUE | NEW_VALUE | ADD_TIME|
-----------------------------------------------
| 1 | A | 2 | 3 | 1/11/2019 8:00:00am |
| 1 | B | 3 | 4 | 12/10/2018 8:00:00am|
| 1 | B | 4 | 5 | 12/11/2018 8:00:00am|
| 2 | A | 5 | 1 | 12/5/2018 08:00:00am|
| 2 | A | 1 | 2 | 12/5/2019 09:00:00am|
| 2 | A | 2 | 3 | 12/5/2019 10:00:00am|
| 2 | B | 1 | 2 | 12/5/2019 10:00:00am|
TableB
| ID | MONTH_END | TYPE_A | TYPE_B |
-----------------------------------
| 1 | 1/31/19 | 3 | 5 |
| 1 | 12/31/18 | 3 | 5 |
| 1 | 11/30/18 | 3 | 5 |
| 2 | 12/31/18 | 3 | 2 |
| 2 | 11/30/18 | 3 | 2 |
Desired Output for TableB
| ID | MONTH_END | TYPE_A | TYPE_B |
-----------------------------------
| 1 | 1/31/19 | 3 | 5 |
| 1 | 12/31/18 | 2 | 5 |
| 1 | 11/30/18 | 2 | 3 |
| 2 | 12/31/18 | 3 | 2 |
| 2 | 11/30/18 | 5 | 2 |
My Query for Type A (Which I plan to adapt for Type B and execute as well for the desired output)
update TableB B
set b.type_a =
(
with aa as
(
select id, nvl(old_value, new_value) typea, add_time
from TableA
where type = 'A'
order by id, add_time ascending
)
select typea
from aa
where aa.id = b.id
and b.month_end <= aa.add_tm
)
where exists
(
with aa as
(
select id, nvl(old_value, new_value) typea, add_time
from TableA
where type = 'A'
order by id, add_time ascending
)
select typea
from aa
where aa.id = b.id
and b.month_end <= aa.add_tm
)
Kudo's for giving example input data and desired output. I found your question a bit confusing so let me rephrase to "Provide the last type a value from table a that is in the same month as the month end.
By matching on type and date of entry, we can get your answer. The "ROWNUM=1" is to limit result set to a single entry in case there is more than one row with the same add_time. This SQL is still a mess, maybe someone else can come up with a better one.
UPDATE tableb b
SET b.typea =
(SELECT old_value
FROM tablea a
WHERE LAST_DAY( TRUNC( a.add_time ) ) = b.month_end
AND TYPE = 'A'
AND add_time =
(SELECT MAX( add_time )
FROM tablea
WHERE TYPE = 'A' AND LAST_DAY( TRUNC( a.add_time ) ) = b.month_end)
AND ROWNUM = 1)
WHERE EXISTS
(SELECT old_value
FROM tablea a
WHERE LAST_DAY( TRUNC( a.add_time ) ) = b.month_end AND TYPE = 'A');

Check if data for update is same as before in SQL Server

I have a table Table1:
ID | RefID | Answer | Points |
----+-------+---------+--------+
1 | 1 | 1 | 5 |
2 | 1 | 2 | 0 |
3 | 1 | 3 | 3 |
4 | 2 | 1 | 4 |
I have a result set in temp table Temp1 with same structure and have update Table1 only if for refID answer and points have changed, otherwise there should be deletion for this record.
I tried:
update table1
set table1.answer = temp1.answer,
table1.points = temp1.points
from table1
join temp1 on table1.refid = temp1.refid
where table1.answer != temp1.answer or table1.points != temp1.points
Here is a fiddle http://sqlfiddle.com/#!18/60424/1/1
However this is not working and don't know how to add the delete condition.
Desired result should be if tables not the same ex. (second row answer 2 points3):
ID | RefID | Answer | Points |
----+-------+---------+--------+
1 | 1 | 1 | 5 |
2 | 1 | 2 | 3 |
3 | 1 | 3 | 3 |
4 | 2 | 1 | 4 |
if they are same all records with refID are deleted.
Explanation when temp1 has this data
ID | RefID | Answer | Points |
----+-------+---------+--------+
12 | 1 | 1 | 5 |
13 | 1 | 2 | 0 |
14 | 1 | 3 | 3 |
EDIT: adding another id column questionid solved the update by adding this also in join.
Table structure is now:
ID | RefID | Qid |Answer | Points |
----+-------+------+-------+--------+
1 | 1 | 10 | 1 | 5 |
2 | 1 | 11 | 2 | 0 |
3 | 1 | 12 | 3 | 3 |
4 | 2 | 11 | 1 | 4 |
SQL for update is: (fiddle http://sqlfiddle.com/#!18/00f87/1/1) :
update table1
set table1.answer = temp1.answer,
table1.points = temp1.points
from table1
join temp1 on table1.refid = temp1.refid and table1.qid = temp1.qid
where table1.answer != temp1.answer or table1.points != temp1.points;
SELECT ID, refid, answer, points
FROM table1
How can I make the deletion case, if data is same ?
You need to add one more condition in the join to exactly match the column.Try this one.
update table1
set table1.answer=temp1.answer,
table1.points=temp1.points
from
table1 join temp1 on table1.refid=temp1.refid and **table1.ID=temp1.ID**
where table1.answer!=temp1.answer or table1.points!=temp1.points
I would first do the delete, and only then the update.
The reason for this is that once you've deleted all the records where the three columns are the same, your update statement becomes simpler - you only need the join, and no where clause:
DELETE t1
FROM table1 AS t1
JOIN temp1 ON t1.refid = temp1.refid
AND t1.qid = temp1.qid
AND t1.answer=temp1.answer
AND t1.points=temp1.points
UPDATE t1
SET answer = temp1.answer,
points = temp1.points
FROM table1 AS t1
JOIN temp1 ON t1.refid=temp1.refid
AND t1.qid = temp1.qid
I think from what i understood that you need to use id instead of refid or both if id is unique

Select from cross-reference based on inclusion (column values being superset)

Given a cross-reference table t relating table a with b:
| id | a_id | b_id |
--------------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 7 |
| 5 | 2 | 3 |
| 6 | 3 | 2 |
| 7 | 3 | 3 |
What would be the conventional way of selecting all a_id whose b_id is a superset of a given set?
For example, for the set (2,3), I would expect the result:
| a_id |
--------
| 1 |
| 3 |
Since a_id 1 and 3 are the only set of b_id that is a superset of (2,3).
The best solution I've found so far (thanks to this answer):
select id
from a
where 2 = (select count(*)
from t
where t.a_id = a.id and t.b_id in (2,3)
);
But I'd prefer to avoid calculating stuff like cardinality before running the query.
You can simply adapt the query as:
select id
from a cross join
(select count(*) as cnt
from t
where . . .
) x
where x.cnt = (select count(*)
from t
where t.a_id = a.id and t.b_id in (2,3)
);

ORACLE Not In on Multiple Columns

Kindly have a look at the following structure.
Table: A
+---------+----------+
| Col1A | Col2A |
+---------+----------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 1 | 2 |
| 2 | 2 |
+---------+----------+
Table: B
+---------+----------+
| Col1B | Col2B |
+---------+----------+
| 2 | 1 |
| 3 | 1 |
+---------+----------+
Here is what I am trying to achieve that is to get the result as following:
Result
+---------+----------+
| Col1A | Col2A |
+---------+----------+
| 1 | 1 |
| 4 | 1 |
| 1 | 2 |
| 2 | 2 |
+---------+----------+
What I want :
I want to get the result of table A records which are not exists in Table B. It should be on the basis of both column. If the combination exist in first table then don't no show it.
What I have tried so far :
The first approach that I have tried was to use Not In statement. Following is my statement.
SELECT A.COL1A, A.COL2A
FROM A
WHERE A.COL1A NOT IN (
SELECT B.COL1B FROM B
);
But the issue with this approach didn't consider the second column. It will give me the following result.
+---------+----------+
| 1 | 1 |
| 4 | 1 |
+---------+----------+
While it will not show following as it should be sub-traced because of we didn't checked for other column.
+---------+----------+
| 1 | 2 |
| 2 | 2 |
+---------+----------+
Then I tried Not Exists but I didn't worked too. Here is my query.
SELECT A.COL1A, A.COL2A
FROM A
WHERE NOT EXISTS(
SELECT B.COL1B,B.COL2B FROM B
);
Edit
Sorry I forget to include the fiddle link.
Here it is : Fiddle Demo
For using Exists I think you should use it like this:
SELECT *
FROM A
WHERE
NOT EXISTS(SELECT 1
FROM B
WHERE A.Col1A = B.Col1B AND A.Col2A = B.Col2B)
One way to do this which is conceptually simple is to JOIN tables A and B on the two columns, and then SELECT only those rows in A which do not exist in B:
SELECT *
FROM A
LEFT OUTER JOIN B
ON (A.COL1A = B.COL1B AND A.COL2A = B.COL2B)
WHERE B.COL1B IS NULL
SELECT *
FROM a
WHERE (col1a, col2a) NOT IN (SELECT col1b, col2b FROM b);
But be aware that this will fail if either col1b or col2b contains NULL values.
SQLFiddle DEMO
SELECT A.COL1A, A.COL2A
FROM A
WHERE (Col1A, Col2A) NOT IN (
SELECT Col1B, Col2B FROM B
);
You can even use:
SELECT A.COL1A, A.COL2A
FROM A
WHERE (Col1A, Col2A) NOT IN (
(2, 1),
(3, 1)
);

How to generate an order index value (as in the order of a list) in a SELECT statement

Suppose I have these two tables :
TABLEA TABLEB
----------- -----------
ID | NAME ID | TABLEA_ID | NAME
1 | ... 1 | 1 | ...
2 | 2 | 2 | ...
3 | 3 | 2 |
4 | 4 | 2 |
5 | 3 |
6 | 3 |
7 | 4 |
8 | 2 |
I want an SQL SELECT statement that can generate such result when TABLEA.ID = TABLEB.TABLEA_ID, you can note here I don't care about grouping or ordering, I just want to generate a incremented value for each line of the same TABLEB.TABLEA_ID.
ID | TABLEA_ID | ORDER_INDEX | NAME
1 | 1 | 0 | ...
2 | 2 | 0 | ...
3 | 2 | 1 |
4 | 2 | 2 |
5 | 3 | 0 |
6 | 3 | 1 |
7 | 4 | 0 |
8 | 2 | 3 |
I tried without success to use rownum in several combination of sub-selects to generate the ORDER_INDEX depending on the value in TABLEA_ID.
Do you have hint to do that in plain SQL, is it even possible with plain SQL.
Is it possible via a PL/SQL ? And how if possible ?
Thank you very much in advance.
I believe that this is what you want:
SELECT B.ID, B.TABLEA_ID,
ROW_NUMBER() OVER(PARTITION BY B.TABLEA_ID ORDER BY B.ID) - 1 ORDER_INDEX,
B.NAME -- OR A.NAME, its not clear on your question
FROM TABLEB B
LEFT JOIN TABLEA A
ON B.TABLEA_ID = A.ID
Something like this:
SELECT
TableB.ID,
TableB.TableA_ID,
ROW_NUMBER() OVER(PARTITION BY TableB.TableA_ID ORDER BY TableB.TableA_ID) AS ORDER_INDEX,
TableB.Name
FROM
TableA
JOIN TableB
ON TableA.ID=TableB.TableA_ID
ORDER BY TableB.ID
How about
ROW_NUMBER() OVER (PARTITION BY TABLEA_ID ORDER BY ID ASC) AS ORDER_INDEX
as the definition of ORDER_INDEX