MySQL: UPDATE ... ON DUPLICATE KEY "ANOTHER UPDATE"? - mysql

I've stumbled on a problem which is supposedly should have a valid solution in a single MySQL query, yet I can't figure it out (I'm far from an expert in MySQL, alas).
The table has the following columns: A, B, C. (A, B) is a primary key (INTs). C - is a string. The table is populated with records.
Under some conditions I need to update a value x in column A to new value y. If some records do already have y in A (can be many at once, for records with different values in B), "duplicate key(s)" condition occurs. In such case, I need to concatenate values in C from old records and corresponding pending updates, for every specific b.
Example:
| A | B | C
| 1 | 2 | ABC
| 1 | 4 | DEF
| 2 | 2 | GHI
| 2 | 4 | JKL
| 2 | 5 | MNO
After update in A as 2 -> 1, I need to get:
| A | B | C
| 1 | 2 | ABCGHI
| 1 | 4 | DEFJKL
| 1 | 5 | MNO
There is a well-known INSERT ... ON DUPLICATE KEY UPDATE statement. But how to handle gracefully the same problem on updates? I'm not sure if and how REPLACE statement or REPLACE function can be used for the task.
Thanks in advance.

I think, whatever happens, you will need two statements:
ALTER TABLE myTable ADD UNIQUE INDEX (A, B);
INSERT INTO myTable (A, B, C)
SELECT 1, B, C
FROM myTable t
WHERE A = 2
ON DUPLICATE KEY UPDATE
C = CONCAT(myTable.C, VALUES(C))
;
DELETE FROM myTable WHERE A = 2;
See it on sqlfiddle.
If there is a risk of concurrency issues, you will obviously need to perform these within a transaction to preserve atomicity.

Related

Count rows referring to a particular row, multiple referencing tables in MySql?

My question is the following:
As asked in the question "How to count amount of rows referring to a particular row foreign key in MySql?", I want to count table references involving multiple tables referring to the table I'm interested about. However here we want the specific number of references per row for the resourced table.
In addition, what about the variant where the tables do reference eachother, but the foreign key does not exist?
Let's setup some minimal examples;
We have three tables, here called A, B, and C. B and C refer rows in A. I want to count the total amount of references for each row in A.
Contents of the first table (A), and expected query results in the column 'Count':;
+----+------------+-------+
| ID | Name | Count |
+----+------------+-------+
| 1 | First row | 0 |
| 2 | Second row | 5 |
| 3 | Third row | 2 |
| 4 | Fourth row | 1 |
+----+------------+-------+
Contents of the second table (B):
+----+------+
| ID | A_ID |
+----+------+
| 1 | 2 |
| 2 | 2 |
| 3 | 2 |
+----+------+
Contents of the third table (C):
+----+------+
| ID | A_ID |
+----+------+
| 1 | 2 |
| 2 | 2 |
| 3 | 3 |
| 4 | 3 |
| 5 | 4 |
+----+------+
Important restrictions for a solution
The solution should work with n tables, for reasonable values of n. The example has n=2.
The solution should not involve a subset of the product set of all the tables. As some rows from A may be referenced a bunch of times in all the other tables the size of the product set may well be stupidly large (e.g. 10*10*10*... becomes big quickly). E.g. it may not be O(q^n) where n is the number of tables and q is the amount of occurrences.
This is a partial solution, which I believe still suffers from performance problems related to condition [2]
I'm adding this as an answer as it may be useful for those working towards a better solution
Apply the following query. Extend as necessary with additional tables, adding additional lines to both the sum and the set of JOINs. This particular solution will work as long as you have less than about 90 tables. With more than that, you will have to run multiple queries like it and cache the results (for example by creating a column in the 'A' table), then sum all these later on.
SELECT
COUNT(DISTINCT B.ID) +
COUNT(DISTINCT C.ID) -- + .....
AS `Count`
FROM A
LEFT JOIN B ON A.ID = B.A_ID
LEFT JOIN C ON A.ID = C.A_ID
Unfortunately, if you have often referenced rows, the query will have a massive intermediate result, run out of memory, and thus never complete.

UPDATE/INSERT INTO/DELETE FROM table in MYSQL

I have a table in MYSQL database TABLE1, with columns COLA, COLB, COLC and (COLA,COLB) are composite primary keys. Something like this
-----------------------
| COLA | COLB | COLC |
-----------------------
| A | B | C |
-----------------------
| A | Q | D |
-----------------------
| A | E | J |
-----------------------
| B | W | P |
-----------------------
Also there is background script which passes data to a java program which should update the table under following conditions :
If new values have any new pair for PRIMARY KEYs then INSERT new row into the table.
If new values have any common pair for PRIMARY KEYs then UPDATE the table.
DELETE all other rows Where COLA value matches with new values.
If new vaues are ('A','B','L'),('A','Y','D'),('A','Q','Z') then it should :
UPDATE 1st and 2nd row.
INSERT a new row ('A','Y','D').
DELETE only 3rd row.
So table should look like
-----------------------
| COLA | COLB | COLC |
-----------------------
| A | B | L |
-----------------------
| A | Q | Z |
-----------------------
| B | W | P |
-----------------------
| A | Y | D |
-----------------------
To implement this I was running two queries :
INSERT INTO TABLE1 VALUES('A','B','L'),('A','Y','D'),('A','Q','Z') ON DUPLICATE KEY UPDATE COLC=VALUES(COLC);
Which is working the way I want. But when I try to delete other rows I am getting into problem what I am trying is :
DELETE FROM TABLE1 WHERE NOT ((COLA='A' AND COLB='B') OR (COLA='A' AND COLB='Y') OR (COLA='A' AND COLB='Q'));
But it does not work. As it deletes the last row as well.
So
How to implement the query?
Can it be clubbed into one query?
THANKS IN ADVANCE :)
I also couldn't find one query solution to this issue but for the second query a bit optimised version can be:
DELETE FROM TABLE1 WHERE COLA='A' AND COLB NOT IN ('B','Y','Q');
or
DELETE FROM TABLE1 WHERE COLA='A' AND COLC NOT IN ('L','Z','D');
Any of the above can be used and it can be a bit scalable than the one you provided.
I got the answer to first question. query should be
DELETE FROM TABLE1 WHERE COLA='A' AND NOT ((COLA='A' AND COLB='B') OR (COLA='A' AND COLB='Y') OR (COLA='A' AND COLB='Q'));

MySql - apply same unique constraint to two different combinations of fields

How do I specify unique constraint on different combinations of fields? E.g.
id | Fld1 | Fld2 | Fld3
-------------------------
1 | A | B | C
-------------------------
2 | A | C | D
I'd like to make the example above illegal because combination (Fld1, Fld3) in row 1 has same values as combination (Fld1, Fld2) in row 2.
Is there any way to do this?
There is no way possible for this except a trigger but performance will be a constraint. You can only have combine unique key but this case will not fullfil.

How to increment a field in MySql using “ON DUPLICATE KEY UPDATE” when each row increment each value?

this question like increment a field with same value
but I want to achieve each row increment each value
for example:
a is a primary key
Original data
a | share_count | read_count |
1 | 2 | 3 |
through
INSERT INTO table (a, share_count,read_count)
VALUES(1,share_count+1,read_count+2),(2,share_count+2,read_count+3)
ON DUPLICATE KEY UPDATE
a=VALUES(a),share_count=VALUES(share_count),read_count=VALUES(read_count)
Goal result
a | share_count | read_count |
1 | 3 | 5 |
2 | 2 | 3 |
I tried,but fail.Thanks for answering
Usually if you try to INSERT you insert count=1 but not share_count+2 or read_count+3.
If I guess your goal correctly you need something like:
http://sqlfiddle.com/#!9/9d4c6/1
INSERT INTO t1 (a, share_count,read_count)
VALUES
(1,1,1),
(2,1,1)
ON DUPLICATE KEY UPDATE
share_count=share_count+1,read_count=read_count+1

Retrieving rows based on a list of IDs in MySQL

I'm trying to retrieve all the rows in a table with children variables where the Foreign Key of those rows is equal to the Primary Key of rows in a table with parent variables.
Graphically it looks something like this:
Table 1. This table contains the parent rows.
ID | variable | variable | etc.
1 | XX | BB | ...
2 | YY | AA | ...
Table 2. This table contains the children rows.
ID | FK (parent) | variable | etc.
1 | 1 | BB | ...
2 | 1 | AA | ...
3 | 1 | AA | ...
4 | 2 | AA | ...
5 | 3 | AA | ...
I'm obviously not an expert in SQL, what I would normally do in another programming language is writing a loop that cycles through every row in the parent table, and then checks the children table if there is a match. I have, however, no idea of what would be the most efficient approach here. The parent table will have 50+ rows. The children table has 8000+ rows.
UPDATE: I want to dump the relevant data from the children table in a new table. So I do not want a combined table with data from the parent and children table, which is what a JOIN does I think.
UPDATE 2: I managed to get what I wanted through:
INSERT INTO NewTable
select columns
from ChildrenTable t
inner join ParentTable p
on t.parentId = p.Id
Thanks for the help!
You can try like this-
Select * from table1 left join table2 on table1.id = table2.fk
You wrote:
I want to dump the relevant data from the children table in a new
table. So I do not want a combined table with data from the parent and
children table, which is what a JOIN does I think.
Well JOIN just combines two or more relevant data from chosen tables. What I mean is you can SELECT whatever columns you want i.e. if you have such tables (a bit updated columns from your original table):
parent-table
ID | variable1 | variable2 | etc.
1 | XX | BB | ...
2 | YY | AA | ...
child-table
ID | FK-ID | variable | etc.
1 | 1 | BB | ...
2 | 1 | AA | ...
3 | 1 | AA | ...
4 | 2 | AA | ...
5 | 3 | AA | ...
And you want to retrieve only ID from first table, variabl2 from first table and variable from second one you would write
SELECT ID.parent-table, variable2.parent-table, variable.child-table
FROM parent-table
JOIN child-table ON parent-table.ID = FK-ID.child-table;
Or if you don't like joins you can ignore them and just get data from both tables and specify where clause i.e.
SELECT ID.parent-table, variable2.parent-table, variable.child-table
FROM parent-table, child-table
WHERE parent-table.ID = FK-ID.child-table;
Both above written queries are equivalent. If you want you can create a new table, let's call it parent-child-table with that data which will be a separate copy. Or if you need to use it a lot you can create a VIEW (you can google about it) which is virtual table (it stores a query), for example let's call it parent-child-view if you make some changes in parent-table and child-table changes will be reflected in parent-child-view but if you create a separate new table parent-child-table changes won't be reflected because it's just a copy.
You can Try using:
select * from table2, table1 where table2.fk = table1.id
select * from child_table_name as tblchild, parent_table_name as tblparent, where tblchild.fk_column_name=tblparent.id