Sqlite update not affecting all rows when using subquery - sql

Based on this answer I am trying to update a column in table b based on values from table a, where table b has a foreign key to table a. My query executes successfully and works as expected on the rows it affects. However, it does not affect all rows, and I do not understand why. It seems to only affect three rows no matter how large the database.
Here is a reproducible example:
CREATE TABLE a ( qty1 INTEGER, qty2 INTEGER, aID INTEGER PRIMARY KEY)WITHOUT ROWID;
CREATE TABLE b ( bID INTEGER, aID INTEGER,FOREIGN KEY(aID) REFERENCES a(aID) ON DELETE CASCADE);
INSERT INTO a (qty1, qty2, aID) VALUES (0, 100, 1906250435150010797);
INSERT INTO a (qty1, qty2, aID) VALUES (100, 100, 1906250435150010798);
INSERT INTO a (qty1, qty2, aID) VALUES (0, 100, 1906250435150010799);
INSERT INTO b (bID, aID) VALUES (0, 1906250435150010797);
INSERT INTO b (bID, aID) VALUES (0, 1906250435150010798);
INSERT INTO b (bID, aID) VALUES (1, 1906250435150010798);
INSERT INTO b (bID, aID) VALUES (0, 1906250435150010799);
ALTER TABLE b ADD COLUMN SameQty INTEGER;
UPDATE b SET SameQty = 0 WHERE aID = (SELECT aID from a where qty1 != qty2);
UPDATE b SET SameQty = 1 WHERE aID = (SELECT aID from a where qty1 = qty2);
Expected result:
Actual result:
Tested on sqlite 3.26.0

Your update statement:
UPDATE b SET SameQty = 0 WHERE aID = (SELECT aID from a where qty1 != qty2);
has a subquery that can return multiple rows, but is used in a context where only a single row is expected. I feel like that should cause an error, but if you're not seeing one, maybe it's just picking a single arbitrary row from the results?
Anyways, you want IN:
UPDATE b SET SameQty = 0 WHERE aID IN (SELECT aID from a where qty1 != qty2);

Your using a subquery that can and does return more than one row in a scalar context when doing "WHERE aID = (SELECT aID from a". Use IN instead of =.
UPDATE b SET SameQty = 0 WHERE aID IN (SELECT aID from a where qty1 != qty2);
UPDATE b SET SameQty = 1 WHERE aID IN (SELECT aID from a where qty1 = qty2);
db<>fiddle

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;

Update rows only when a join is unambiguous

I've got two tables, a and b, both with product_name and value. The value column in a is null.
I'd like to update the a table with values from the b table. Because of a quirk in the data, product_name is not unique in either table.
I only want to set the value when there is one unambiguous match on product_name between the two. When more than one row in a has the same product name, or more than one row matches from b, I'd like to keep the value empty. Is there an efficient way to do this in Postgres?
A simpler version of this would be to first identify unique product names in a. Then, update rows where only a single row in b matches -- but I'm also not sure how to write that constraint.
The simple way:
update a
set value = (select min(b.value) from b where b.product_name = a.product_name)
where product_name in (select product_name from a group by product_name having count(*) = 1)
and product_name in (select product_name from b group by product_name having count(*) = 1)
;
You can use aggregation:
update a
set value = b.value
from (select b.product_name, max(b.value) as value
from b
group by b.product_name
having min(b.value) = max(b.value) -- there is only one value
) b
where b.product_name = a.product_name;
Note that this assumes that b.value is not null. It is easy to include logic for null values, if that is needed.
Just put together [How to Select Every Row Where Column Value is NOT Distinct and #Gordon Linoff's answer:
create table a (
id serial primary key
,product_name text
,value int
);
create table b (
id serial primary key
,product_name text
,value int not null
);
insert into a (product_name) values
('A')
,('B')
,('C')
,('D')
,('E')
,('E');
insert into b (product_name,value) values
('A',1)
,('A',1)
,('B',42)
,('C',1)
,('C',2)
,('E',1)
;
update a
set value = b.value
from (select product_name, min(value) as value
from b
group by b.product_name
having 1 = count(*)
) b
where b.product_name = a.product_name
and a.product_name not in
(select product_name
from a
group by product_name
having 1 < count(*));
#Gordon Lindof your answer fails if product_name and value both dupilcated in b (example product_name=A) and misses the requirement product_name not duplicated in a.

How to perform UPDATE + Mathematical function into empty column?

I need perform a simple subtraction on a column and insert the new value into a new table.column.
The table are structured as such, logic is not a column it is what I want to do mathematically just for time not the date, dropping it is fine as well but the date and time are in the same cell stuck together:
Table A
A Time (Logic)
1 aaa YYYY-MM-DD HH:MM:SS (row2 - row1)
2 aaa YYYY-MM-DD HH:MM:SS (row3-row2)
3 aaa YYYY-MM-DD HH:MM:SS (row4-row3)
Table B
A new_time
1 aaa insert logic row 1
2 aaa insert logic row 2
3 aaa insert logic row 3
Question is how do I subtract each row for tableA.time from the next row and UPDATE that value into tableB.new_time?
-----EDIT----
#klin
this is my script modeled after yours:
update tableB
set new_time = tableA.time
from (
select tableA.A- lead(tableA.A) over (order by tableB.B)
from tableA
) tableA.A
where tableB.a = tableA.A
Error Message:
SQL Error [500310] [42P10]: [Amazon](500310) Invalid operation: subquery in FROM may not refer to other relations of same query level;
You can use a subquery as a data source in update. Example:
create table table_a (id int primary key, t timestamp);
insert into table_a values
(1, '2016-09-01'), (2, '2016-09-02'), (3, '2016-09-04');
create table table_b (id int primary key, i interval);
insert into table_b values
(1, null), (2, null), (3, null);
update table_b b
set i = a.i
from (
select id, t - lead(t) over (order by id) i
from table_a
) a
where b.id = a.id;
select * from table_b;
id | i
----+---------
1 | -1 days
2 | -2 days
3 |
(3 rows)
One-letter aliases are handy but sometimes not quite clear. The same query with more informative aliases:
update table_b as alias_b
set i = subquery.i
from (
select id, t - lead(t) over (order by id) i
from table_a
) as subquery
where alias_b.id = subquery.id;

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

Interchange value of 2 row in sql table

I have a table which has column name course. In 2nd row course is "C++" and in 4th row course is "ASP.net".
I want to interchange that to value with update query. How can I achieve this?
You can change the values with update, like:
update YourTable set Course = 'ASP.NET' where id = 2
update YourTable set Course = 'C++' where id = 4
or:
update YourTable
set Course =
case id
when 2 then 'ASP.NET'
when 4 then 'C++'
end
where id in (2,4)
Test table and data
create table YourTable(id int primary key, course varchar(10))
insert into YourTable values (1, 'Delphi')
insert into YourTable values (2, 'C++')
insert into YourTable values (3, 'Clipper')
insert into YourTable values (4, 'ASP.net')
Update to switch 2 and 4
update YourTable set
course = case id
when 4 then (select course from YourTable where id = 2)
when 2 then (select course from YourTable where id = 4)
else T.course
end
from
YourTable as T
where T.id in (2, 4)
Result
id course
1 Delphi
2 ASP.net
3 Clipper
4 C++