Update Table: Copy result from one row to another in oracle - sql

i am facing the following problem when updating data in a table.
First the structure of the database.
I have 2 tables.
Table 1 :
Votings: Columns: PKID, RESULT, USAGE, GROUP
For each usage a group has to vote and the result is saved in this table.
Table 2:
Usages: Columns: PKID, STATUS, SOURCE
Each usage has a status, some usages have a source usage, but it can be null.
Now i need to migrate my data. I want to copy certain results from some votings to some other votings in the votings table.
And here i'm stuck...
The use case is the following:
Every voting with a usage in status 4 (i call it active) and group b should get the result of the voting of group b which is bound to the usage whose source is the usage in status 4 (the active usage).
These are the the results i want to insert:
select v2.result from votings v2
join usage us on us.pkid = v2.usage
where us.status=10 and us.source in (
select v3.usage from votings v3
join usage us2 on us2.pkid = v3.usage
where us2.status = 4 and v3.group = v2.group)
But now i'm not able to link these results to the correct row...
Here an example:
example merge

If I understood everything correctly You need merge similiar to this:
merge into votings v
using (select u2.pkid usid, vt.grp, vt.result
from votings vt
join usage u1 on vt.usage = u1.pkid and u1.status = 10
join usage u2 on u2.source = u1.pkid and u2.status = 4
) s
on (v.usage = s.usid and v.grp = s.grp)
when matched then update set result = s.result
Test data:
create table votings(PKID varchar2(3), RESULT int, USAGE varchar2(3), GRP varchar2(2));
insert into votings values ('V01', 23, 'U01', 'B');
insert into votings values ('V02', 17, 'U02', 'B');
insert into votings values ('V03', 9, 'U04', 'C');
insert into votings values ('V04', 3, 'U04', 'B');
create table usage (PKID varchar2(3), STATUS int, SOURCE varchar2(3));
insert into usage values ('U01', 10, null);
insert into usage values ('U02', 10, null);
insert into usage values ('U04', 4, 'U01');
After update value 3 for key V04 was replaced with 23 (key V01).

Related

Postgresql update column based on set of values from another table

Dummy data to illustrate my problem:
create table table1 (category_id int,unit varchar,is_valid bool);
insert into table1 (category_id, unit, is_valid)
VALUES (1, 'a', true), (2, 'z', true);
create table table2 (category_id int,unit varchar);
insert into table2 (category_id, unit)
values(1, 'a'),(1, 'b'),(1, 'c'),(2, 'd'),(2, 'e');
So the data looks like:
Table 1:
category_id
unit
is_valid
1
a
true
2
z
true
Table 2:
category_id
unit
1
a
1
b
1
c
2
d
2
e
I want to update the is_valid column in Table 1, if the category_id/unit combination from Table 1 doesn't match any of the rows in Table 2. For example, the first row in Table 1 is valid, since (1, a) is in Table 2. However, the second row in Table 1 is not valid, since (2, z) is not in Table 2.
How can I update the column using postgresql? I tried a few different where clauses of the form
UPDATE table1 SET is_valid = false WHERE...
but I cannot get a WHERE clause that works how I want.
You can just set the value of is_valid the the result of a ` where exists (select ...). See Demo.
update table1 t1
set is_valid = exists (select null
from table2 t2
where (t2.category_id, t2.unit) = (t1.category_id, t1.unit)
);
NOTES:
Advantage: Query correctly sets the is_valid column regardless of the current value and is a vary simple query.
Disadvantage: Query sets the value of is_valid for every row in the table; even thoes already correctly set.
You need to decide whether the disadvantage out ways the advantage. If so then the same basic technique in a much more complicated query:
with to_valid (category_id, unit, is_valid) as
(select category_id
, unit
, exists (select null
from table2 t2
where (t2.category_id, t2.unit) = (t1.category_id, t1.unit)
)
from table1 t1
)
update table1 tu
set is_valid = to_valid.is_valid
from to_valid
where (tu.category_id, tu.unit) = (to_valid.category_id, to_valid.unit)
and tu.is_valid is distinct from to_valid.is_valid;

Merge Statement with conditions, IBM DB2 sql merge

I am curious if this would work. I'm currently locked out of the target table and am losing development time, so I can't test it for another week and want to move onto the next step.
The goal is to do as follows:
Using 3 primary keys(cch_id, event_type, event_time) I need to compare two tables of data(source_tb and target_tb), then update the second table(Target_tb) based on the following 3 criteria:
1. For matching data, do not do anything
2. For records coming from target_tb but not coming from source_tb, expire the records aka set
event_type = ‘EXPIRED’
3. For records coming from source flow but not available in the target table, insert it.
MERGE target_tb as b USING source_tb as a
ON (a.cch_id = b.cch_id, a.event_type = b.event_type, a.event_time = b.event_time)
WHEN MATCHED
THEN null//do nothing
WHEN NOT MATCHED by b.cch_id THEN b.event_type = 'EXP' WHEN NOT MATCHED BY a.cch_id THEN INSERT a.*
You can't achieve this with MERGE because you need to update non-matching rows in target_tb.
If you still want to do this with a single statement, you may use select from data-change operation like below:
create table target_tb (key int not null, val int);
create table source_tb (key int not null, val int);
insert into target_tb values (1, 1), (2, 2);
insert into source_tb values (3, 3), (2, 2);
with
u as
(
select key
from old table
(
update target_tb t
set val = -1
where not exists (select 1 from source_tb s where s.key = t.key)
)
)
, i as
(
select key
from new table
(
insert into target_tb
select key, val
from source_tb s
where not exists (select 1 from target_tb t where t.key = s.key)
)
)
select a
from (values 1) t(a);
select * from target_tb;
The result is:
|KEY |VAL |
|-----------|-----------|
|1 |-1 |
|2 |2 |
|3 |3 |
dbfiddle link.
I'd suggest using DB Fiddle..
https://dbfiddle.uk/?rdbms=db2_11.1
But no I don't believe your statement will work. Specifically the
For records coming from target_tb but not coming from source_tb, expire the records aka set event_type = ‘EXPIRED’
Db2 merge doesn't support WHEN NOT MATCHED BY SOURCE clause that SQL Server supports...

Adding a LEFT JOIN on a INSERT INTO....RETURNING

My query Inserts a value and returns the new row inserted
INSERT INTO
event_comments(date_posted, e_id, created_by, parent_id, body, num_likes, thread_id)
VALUES(1575770277, 1, '9e028aaa-d265-4e27-9528-30858ed8c13d', 9, 'December 7th', 0, 'zRfs2I')
RETURNING comment_id, date_posted, e_id, created_by, parent_id, body, num_likes, thread_id
I want to join the created_by with the user_id from my user's table.
SELECT * from users WHERE user_id = created_by
Is it possible to join that new returning row with another table row?
Consider using a WITH structure to pass the data from the insert to a query that can then be joined.
Example:
-- Setup some initial tables
create table colors (
id SERIAL primary key,
color VARCHAR UNIQUE
);
create table animals (
id SERIAL primary key,
a_id INTEGER references colors(id),
animal VARCHAR UNIQUE
);
-- provide some initial data in colors
insert into colors (color) values ('red'), ('green'), ('blue');
-- Store returned data in inserted_animal for use in next query
with inserted_animal as (
-- Insert a new record into animals
insert into animals (a_id, animal) values (3, 'fish') returning *
) select * from inserted_animal
left join colors on inserted_animal.a_id = colors.id;
-- Output
-- id | a_id | animal | id | color
-- 1 | 3 | fish | 3 | blue
Explanation:
A WITH query allows a record returned from an initial query, including data returned from a RETURNING clause, which is stored in a temporary table that can be accessed in the expression that follows it to continue work on it, including using a JOIN expression.
You were right, I misunderstood
This should do it:
DECLARE mycreated_by event_comments.created_by%TYPE;
INSERT INTO
event_comments(date_posted, e_id, created_by, parent_id, body, num_likes, thread_id)
VALUES(1575770277, 1, '9e028aaa-d265-4e27-9528-30858ed8c13d', 9, 'December 7th', 0, 'zRfs2I')
RETURNING created_by into mycreated_by
SELECT * from users WHERE user_id = mycreated_by

Merge table in Oracle with delete condition refering to source table

For the following question it is said that the answer should be C. But I think the correct answer is Answer D as NOT MATCHED block inserts all unmatching records to target table. Can anybody explain this?
Thank you.
Q)View the Exhibit and examine the data in ORDERS_MASTER and MONTHLY_ORDERS tables.
Evaluate the following MERGE statement:
MERGE INTO orders_master o
USING monthly_orders m
ON (o.order_id = m.order_id)
WHEN MATCHED THEN
UPDATE SET o.order_total = m.order_total
DELETE WHERE (m.order_total IS NULL)
WHEN NOT MATCHED THEN
INSERT VALUES (m.order_id, m.order_total);
What would be the outcome of the above statement?
A. The ORDERS_MASTER table would contain the ORDER_IDs 1 and 2.
B. The ORDERS_MASTER table would contain the ORDER_IDs 1,2 and 3.
C. The ORDERS_MASTER table would contain the ORDER_IDs 1,2 and 4.
D. The ORDERS_MASTER table would contain the ORDER IDs 1,2,3 and 4.
Answer: C
The correct answer is indeed C, this is because the source of the merge operation is the monthly_orders table, which only contains two records with order_id 2 and 3 respectively.
Think about what will happen for each of these records:
For order_id = 2, because this id exists in the order_master table, we'll execute the MATCHED part of the merge statement, updating the order_total to 2500. Since the quantity for this record is not NULL, the DELETE won't do anything.
For order_id = 3, again, the id exists in the order_master table, so we execute the MATCHED part of the merge statement, updating the order_total to NULL and then issuing a DELETE on order_master for the row we just updated because the quantity on monthly_order is NULL.
This leaves us with order_id 1, 2 and 4, which matches answer C.
Code
CREATE TABLE orders_master (
order_id NUMBER(1) NOT NULL
,order_total NUMBER(10) NULL
)
/
CREATE TABLE monthly_orders (
order_id NUMBER(1) NOT NULL
,order_total NUMBER(10) NULL
)
/
INSERT INTO orders_master (order_id, order_total) VALUES (1, 1000)
/
INSERT INTO orders_master (order_id, order_total) VALUES (2, 2000)
/
INSERT INTO orders_master (order_id, order_total) VALUES (3, 3000)
/
INSERT INTO orders_master (order_id, order_total) VALUES (4, NULL)
/
INSERT INTO monthly_orders (order_id, order_total) VALUES (2, 2500)
/
INSERT INTO monthly_orders (order_id, order_total) VALUES (3, NULL)
/
MERGE INTO orders_master o
USING monthly_orders m
ON (o.order_id = m.order_id)
WHEN MATCHED THEN
UPDATE SET o.order_total = m.order_total
DELETE WHERE m.order_total IS NULL
WHEN NOT MATCHED THEN
INSERT VALUES (m.order_id, m.order_total)
/
COMMIT
/
SQL> select * from orders_master
2 /
ORDER_ID ORDER_TOTAL
---------- -----------
1 1000
2 2500
4

Find rows with same ID and have a particular set of names

EDIT:
I have a table with 3 rows like so.
ID NAME REV
1 A 0
1 B 0
1 C 0
2 A 1
2 B 0
2 C 0
3 A 1
3 B 1
I want to find the ID wich has a particular set of Names and the REV is same
example:
Edit2: GBN's solution would have worked perfectly, but since i do not have the access to create new tables. The added constraint is that no new tables can be created.
if input = A,B then output is 3
if input = A ,B,C then output is 1 and not 1,2 since the rev level differs in 2.
The simplest way is to compare a COUNT per ID with the number of elements in your list:
SELECT
ID
FROM
MyTable
WHERE
NAME IN ('A', 'B', 'C')
GROUP BY
ID
HAVING
COUNT(*) = 3;
Note: ORDER BY isn't needed and goes after the HAVING if needed
Edit, with question update. In MySQL, it's easier to use a separate table for search terms
DROP TABLE IF EXISTS gbn;
CREATE TABLE gbn (ID INT, `name` VARCHAR(100), REV INT);
INSERT gbn VALUES (1, 'A', 0);
INSERT gbn VALUES (1, 'B', 0);
INSERT gbn VALUES (1, 'C', 0);
INSERT gbn VALUES (2, 'A', 1);
INSERT gbn VALUES (2, 'B', 0);
INSERT gbn VALUES (2, 'C', 0);
INSERT gbn VALUES (3, 'A', 0);
INSERT gbn VALUES (3, 'B', 0);
DROP TABLE IF EXISTS gbn1;
CREATE TABLE gbn1 ( `name` VARCHAR(100));
INSERT gbn1 VALUES ('A');
INSERT gbn1 VALUES ('B');
SELECT
gbn.ID
FROM
gbn
LEFT JOIN
gbn1 ON gbn.`name` = gbn1.`name`
GROUP BY
gbn.ID
HAVING
COUNT(*) = (SELECT COUNT(*) FROM gbn1)
AND MIN(gbn.REV) = MAX(gbn.REV);
INSERT gbn1 VALUES ('C');
SELECT
gbn.ID
FROM
gbn
LEFT JOIN
gbn1 ON gbn.`name` = gbn1.`name`
GROUP BY
gbn.ID
HAVING
COUNT(*) = (SELECT COUNT(*) FROM gbn1)
AND MIN(gbn.REV) = MAX(gbn.REV);
Edit 2, without extra table, use a derived (inline) table:
SELECT
gbn.ID
FROM
gbn
LEFT JOIN
(SELECT 'A' AS `name`
UNION ALL SELECT 'B'
UNION ALL SELECT 'C'
) gbn1 ON gbn.`name` = gbn1.`name`
GROUP BY
gbn.ID
HAVING
COUNT(*) = 3 -- matches number of elements in gbn1 derived table
AND MIN(gbn.REV) = MAX(gbn.REV);
Similar to gbn, but allowing for the possibility of duplicate ID/Name combinations:
SELECT ID
FROM MyTable
WHERE NAME IN ('A', 'B', 'C')
GROUP BY ID
HAVING COUNT(DISTINCT NAME) = 3;
OKAY!... I solved my problem ! I modified GBN's logic to do it without a search table using the IN clause
1 flaw with doing MAX(rev) = MIN(REV) is: if i have a data like so .
ID NAME REV
1 A 0
1 B 1
1 A 1
then when I use a query like
Select ID from TABLE
where NAME in {A,B}
groupby ID
having count(*) = 2
and MIN(REV) = MAX(REV)
it will not show me the ID 1 as the min and max are different and the count is 3.
So i simply add another column to the groupby
so the final query is
Select ID from TABLE
where NAME in {A,B}
groupby ID,REV
having count(*) = 2
and MIN(REV) = MAX(REV)
Thanks,to all that helped. !