Copy multiple records using foreign keys - mysql

Suppose I have two tabels, A and B, each with three columns (A.id, A.title, A.text and B.id, B.a_id, B.text). B.a_id is a foreign key to relates to A.id. Now, suppose there is one record in A (1, 'foo', 'bar') and 2 records in B (1, 1, 'test') and (2, 1, 'test1').
My question is, is there a standard method of copying the record in A, and, at the same time copying all the records from B that relate to A. So suppose I create a new record in A (2, 'foo', 'bar') that's based on (1, 'foo', 'bar'), is there some sort of method that creates two new records in B (3, 2, 'test') and (4, 2, 'test1)?
I've never used triggers before, is this the correct moment to start doing that? Or is this a very stupid question?

this is not a stupid question. However, I believe that this is not possible with pure SQL, or only with some exotic syntax that I am not aware of. Copying rows is not the problem (assuming that id is auto_increment):
insert into A (title, text) select title, text from A where id = XY
However, then you need to find the last insert ID to duplicate the records in B. Let's see:
insert into B (a_id, text) select LAST_INSERT_ID(), text from B where a_id = XY
Hm... maybe this works, but I am a bit sceptical about the LAST_INSERT_ID(). Anyway, I don't think it can be done with just one statement.
Let me know how it goes
Tom

Related

avoid duplicate in mysql insert

I have a friends table that has 'id', 'friend (INTEGER)' and 'user (INTEGER)' fields.
A friend relationship exists between user and friend.
i.e.
id user friend
6 22 45
7 45 22
is the same friend relationship and should be considered a duplicate record.
I want to input a whole lot of records at once, so something like:
INSERT INTO friends (user, friend) VALUES(22, 34), (22, 76), (22, 567)...;
In this situation, I can easily use IGNORE to avoid entering a duplicate (22, 34) entry(if (22, 34) already exists), but is there a way I can also avoid entering (22, 34) if (34, 22) already exists, as this is the same relationship.
Sort each pair; then do INSERT IGNORE to avoid error messages.
You can sort as you insert by doing
INSERT IGNORE INTO tbl (a,b) VALUES (LEAST($a, $b), GREATEST($a, $b));
However, in order to do a batch insert, you should probably sort in the client language.
Another issue: INSERT IGNORE will create an id before it checks for dup. Therefore, lots of AUTO_INCREMENT values will be 'burned'.
Rather than explaining how to avoid the burning, I will say that there seems to be no good reason for id. Instead have
PRIMARY KEY(user_id, friend_id)
Another issue with the sorting... SELECT may need to do a UNION:
( SELECT ... WHERE user_id = $x )
UNION ALL
( SELECT ... WHERE friend_id = $x )
That implies that you need this, too:
INDEX(friend_id, user_id)

mysql only execute insert if values exist in other table

There are a few questions that look a lot like the question below, but i can't find a proper answer.
I want to do an insert in table b, for example ID_b,ID_a, name, name2.
I only want to do the insert if the ID_a exists in table a
Simple question, leading to a headache.
You can use a single insert-select statement for this.
E.g., assume your values for [ID_b, ID_a, name, name2] are [1, 100, 'name', 'name2']:
INSERT INTO b (ID_b, ID_a, name, name2)
SELECT 1, ID_a, 'name', 'name2'
FROM a
WHERE ID_a = 100
If ID_a = 100 exists, the new row will be inserted. If it does not, zero rows will selected from table a, and thus, nothing will inserted into b.

Inserting last record fields in the next record in mysql

I have a table in mysql. It has a few records from before. Its fields are a, b and c. Sometimes I insert a record in this table like for example this with different values:
$query = "INSERT INTO table(a, b, c) VALUES('1', '0', '2')";
Values are characters. Seems last record in the table is 5, 6, 4. I mean a=5, b=6 and c=4 and I want to insert a new record. My values are 1 and 0 and 2 but I want you to help me for this method:
When b == 0, I don't want to save it and instead, I want to save the last field in the table. For example I am inserting 1, 0, 2 and it just insert a=1 and c=2 but it inserts the last field in table instead this 0 that it is 6.
something like this:
if(false)
{
$query = "INSERT INTO table(a, b, c) VALUES('1', The Last Value In Table, '2')";
}
I would rather not to read last record of table and use its value because it can decrease the speed for me and speed is very important. Its better to use a mysql command that does it automatically.
As you need the data of the last inserted record you must get it from somewhere. The first thing that comes to mind is: read the table and look for the last inserted record, which is something you don't want, maybe because the table is too large to access the data quickly.
So you need a lookup table, containing only the last inserted values (i.e. one record):
create table last_insert_mytable(a varchar, b varchar, c varchar);
Which you fill with a trigger:
create trigger trg_mytable_last_insert after insert on mytable
for each row begin
delete from last_insert_mytable;
insert into last_insert_mytable (a, b, c) values (new.a, new.b, new.c);
end;
So your insert statement looks like this:
insert into mytable(a, b, c)
values ('1', (select b from last_insert_mytable), '2');
or (provided there is already a record in last_insert_mytable):
insert into mytable(a, b, c)
select '1', b, '2' from last_insert_mytable;
Keep in mind that every insert gets a bit slowed down due to the trigger. As there is only one record to deal with, it can be faster than to have to look up the last inserted record in mytable. This depends on the size of mytable. This happens for every insert into mytable. If it is rather seldom to have to look up the latest record, it may be better to have a slow lookup every now and then, than to have a slightly slower insert every time. Well, just try it.

MySQL: show strings in result that were not found

I have a MySQL db table with a column containing strings. And I have a list of strings. Some of the strings from the list can be found in my table, others can't. See my table TableName:
<TableName>
IntegerColumn, StringColumn
1, one
2, two
3, three
4, four
If I execute the query
SELECT * FROM TableName WHERE StringColumn NOT IN ('three', 'four', 'five', 'six');
I get a result of two rows, which contain nothing but NULL.
How can I see for which of the strings there was no match? Because I want to add them to the db table
Thx in advance
Using the following sample
CREATE TABLE sample_table
(
id int auto_increment primary key,
details varchar(30)
);
INSERT INTO sample_table
(id, details)
VALUES
(1, 'One'),
(2, 'Two'),
(3, 'Three'),
(4, 'Four'),
(5, 'Five');
I ran the query
SELECT * FROM sample_table
WHERE details NOT IN ('two', 'three', 'nine');
which gave the correct output of:
1 One
4 Four
5 Five
If you've got NULL returned then there is something you're not explaining in your question. Can you provide schema information or even a SQL Fiddle and I'm sure you'll get a much better answer.
I think what you want is, 'three', 'four', 'five', 'six' if any of this string is not present in the database you want to identify that.
Using query I think it will be tough. You can just use below query to get the available strings and the counts. Then, if you are using a programming language you can identify which string are not present in the result and then proceed further.
SELECT StringColumn, count(*) FROM TableName group by StringColumn
Not sure if this is what you are looking for.
This should not go this way. Check the following demo to ensure that your code is correct. Now what really matters is the set of data present in the table.
DEMO
Here is the DDL:
create table tab1 (IntegerColumn int(2), StringColumn varchar(20));
insert into tab1 values(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four');

How do I select an ID by its result set or get a better data model?

I'm having a little trouble with a selection and I was figuring I could either look for help in solving the selection or find a better way to model my data. My tables are structured such that:
Table A( a_id, a2, a3, a4) pk: a_id
Table B( b_id, a_id, b3) pk: b_id, a_id
Table B can have any number of entries for each b_id, but only one for each b_id, a_id. I want to be able to reference the set for each b_id to check for their existence so that the set is not duplicated. For example, say I had a tuple in table C
Table C( c_id, b_id ) pk:c_id
with a reference to a b_id of 1. If another tuple was to be inserted into C which results in the insertion of the same set represented by a b_id of 1 into table B, I would want the new tuple to have a b_id of 1, as well, instead of inserting into table B and using that b_id.
edit:
See this sqlfiddle. Say I wanted to insert a new object which is represented by the following inserts:
INSERT INTO B VALUES (3, 1, 2);
INSERT INTO B VALUES (3, 2, 11;
INSERT INTO B VALUES (3, 3, 5);
INSERT INTO C VALUES (2,3);
How can I query the database (or restructure) so that I can realize that the sets in Table B represented by a b_id of 1 or 3 would be the same? I would then want to change my logic so that the object being inserted is represented by the single statement:
INSERT INTO C VALUES (2,1);
A real-world-like example:
Imagine a player in a game. Each player in the game is a tuple in Table C. Each player can where any number of clothes - Table B. A piece of clothing is defined by the part of the body it covers (Table A) and its color (b3)
I want to find the player wearing a specific set of clothing. Lets say that player wore that same set again - I shouldnt have to add more data to table B, I should be able to say he wore it last game, so we'll just reference that set of clothing
You need programming language eg PHP to loop all the conditions:
SELECT B.b_id, BB.num, count(*)
FROM B, (
SELECT b_id, count(*) num
FROM B
WHERE (a_id=1 and b3=1)
OR (a_id=2 and b3=11)
OR (a_id=3 and b3=5)
OR (a_id=4 and b3=6)
-- you need programming language eg php to loop all your set data here
GROUP BY 1
) as BB
WHERE B.b_id = BB.b_id
GROUP BY 1,2
HAVING count(*) = 4 and count(*) = BB.num
-- count(*) should be manually input to match above loop of OR
The sub-query get b_id and count, join back with B to match if they are exactly same.
Which means, you need to provide exactly same set of values [a_id, b3] to get correct value of b_id, not sub-set, not sup-set, exactly match.
In your example data, if you want to return bid=1, you need provide 3 sets of [aid,b3); if you want to return bid=2, you need to provide 4 sets of [aid,b3]
If b_id, a_id do not constitute a unique identity for table B then you don't really have a pk.
Anyways, adding UNIQUE (a_id, b3) to your table B definition will prevent the duplicate entry values:
INSERT INTO B VALUES (3, 1, 2);
INSERT INTO B VALUES (3, 2, 8);
INSERT INTO B VALUES (3, 3, 10);
A foreign key constraint would then prevent:
INSERT INTO C VALUES (2,3);
But I don't think this approach is sufficient for what you are trying to do. For example would the insert into B above be ok if the existing records for B included another record, say 1, 5, 20?