tags:

views:

206

answers:

4

I have two MySql tables as shown below with the data shown:

CREATE TABLE `A` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`status` varchar(50) DEFAULT NULL,
`another_field` varchar(50) DEFAULT NULL
)

INSERT INTO `A` VALUES ('1', null, 'a');
INSERT INTO `A` VALUES ('2', null, 'b');
INSERT INTO `A` VALUES ('3', null, 'c');


CREATE TABLE `B` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`status` varchar(50) DEFAULT NULL,
`tableA_id` int(12) DEFAULT NULL,
PRIMARY KEY (`id`)
)

INSERT INTO `B` VALUES ('1', 'aa', '1');
INSERT INTO `B` VALUES ('2', 'aa', '1');
INSERT INTO `B` VALUES ('3', 'aa', '2');
INSERT INTO `B` VALUES ('4', 'aa', '3');
INSERT INTO `B` VALUES ('5', 'bb', '3');

I want to know if it is possible to update A.status if all B.status are the same when A.id = B.tableA_id using a single query?

This is what I want my table A to look like:

('1', 'aa', 'a') - Status is updated to 'aa' as B.id 1 & 2 have the same status and same B.tableA_id value.
('2', 'aa', 'b') - Status is updated to 'aa' as B.id 3 has the same status.
('3', null, 'c') - This is not updated because B.id 4 & 5 have different status and the same table2.table1_id value.

Thanks

+3  A: 
UPDATE A
SET    status = COALESCE((
           SELECT MAX(B.status)
           FROM   B
           WHERE  B.tableA_id = A.id
           HAVING MAX(B.status) = MIN(B.status)
       ), A.status)

(Note: I added a correction, you need the COALESCE(..., A.status) or otherwise the status will be set to NULL in case there were multiple statuses in B

Roland Bouman
+1, Very neat query.
gameover
A: 

Not sure about MySql but in MSSQL you could write something like:

UPDATE A SET A.Status = 'aa' FROM A INNER JOIN B on A.id = B.tableA_id WHERE b.status = 'aa'

It should be similar in MySQL, but I'm not if the language supports joins on update. But still I hope it helps.

Fedor Hajdu
THis syntax works in MySQL, but this query does not give the correct result. It will set A(3).status = 'aa', even though there two differrent status values for B(3): 'aa' and 'bb'.
Roland Bouman
A: 
UPDATE a SET status = 
    (
        SELECT status FROM b WHERE tableA_id = a.id LIMIT 0,1
    )
WHERE id IN
    (
        SELECT tableA_id FROM b
        GROUP BY tableA_id
        HAVING COUNT(DISTINCT status) = 1
    )

Update: Roland was right; I have updated the query and it now yields the correct results.

Anax
This query is not correct. The subquery used in the assignment can and will return a set. you need to add either an aggregate function like MIN(status) or MAX(status), add a distinct keyword (DISTINCT status) or add GROUP BY status after the where.
Roland Bouman
I ran some tests and the query behaves as it should. Can you provide a specific dataset which will make the query behave abnormally?
Anax
Anax, in retrospect, indeed your subquery does not return a set. But that's only so because you have another problem: the WHERE clause of the first subquery: `WHERE b.tableA_id = id` is wrong. The reference to `id` should be bound to `A.id`, but it is not - because it is not aliased, it refers to `B.id`. So accidentally, it always yields at most one row. If you correct your query and run it, you'll get ERROR 1242 (21000): Subquery returns more than 1 row.
Roland Bouman
I did try your suggestions; every time I get correct results. First I tried to rename all table a fields by putting the string "a_" in front of each field and the query ran without problems. Then I reverted back to the original field names and I aliased table a by running the query "UPDATE a as NewTable SET NewTable.status = (SELECT status FROM b WHERE b.tableA_id = NewTable.id) WHERE NewTable.id IN (SELECT tableA_id FROM b GROUP BY tableA_id HAVING COUNT(DISTINCT status) = 1)", which once again ran without errors. I'm using MySQL version 5.1.39CE
Anax
Anax, I don't know why you can't reproduce the result. I can. But look at it this way. Let's take the data from the OP. Well, first, your WHERE subquery is evaluated, and this yields (1,2) because there are 2 distinct values for status for b.id=3. Now, the rows where A.id is 1 and 2 are scanned, and for each of them the subquery in the SET clause is evaluated. Well, immediately for A.id = 1, you get 2 rows from B (B.id's: 1,2). That's a set, and not valid in a subquery. If you like to discuss further, feel free to email: [email protected]. Cheers!
Roland Bouman
You were right; it was my eyes playing tricks on me. I have updated my answer.
Anax
A: 
CREATE TABLE `A` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`status` varchar(50) DEFAULT NULL,
`another_field` varchar(50) DEFAULT NULL
)

INSERT INTO `A` VALUES ('1', null, 'a');
INSERT INTO `A` VALUES ('2', null, 'b');
INSERT INTO `A` VALUES ('3', null, 'c');


CREATE TABLE `B` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`status` varchar(50) DEFAULT NULL,
`tableA_id` int(12) DEFAULT NULL,
PRIMARY KEY (`id`)
)

INSERT INTO `B` VALUES ('1', 'aa', '1');
INSERT INTO `B` VALUES ('2', 'aa', '1');
INSERT INTO `B` VALUES ('3', 'aa', '2');
INSERT INTO `B` VALUES ('4', 'aa', '3');
INSERT INTO `B` VALUES ('5', 'bb', '3');
Tejas1810