Many to many relationship defaulting to "all" - mysql

I have to tables A and B. I also have an associative table A_B that stores the relationships between table A and B. In table A, there are records that I want to relate to all records in table B (even records that can be added in the future).
Let's say I have A1, A2, A3 and B1, B2 and B3.
I want A1 related to B1 and B3
A2 related to B1 and B2
A3 related to all Bs
Then the A_B table will look like:
A1, B1
A1, B3
A2, B1
A2, B2
A3, B1
A3, B2
A3, B3
If I add B4, I will need to add a new record in A_B table to relate it to A3. How can I define "A3 is related to all Bs" without requiring the A_B table? I was thinking in adding a flag in A (a boolean is_related_to_all) but I think it looks wrong (I may end up with hundreds of is_related_to_all = false)
Is there any way of doing this using relationships?
EDIT
To add a bit of more of context:
Table A stores partners and Table B stores websites. Some partners are related to specific websites and some partners are related to all websites (and some partners may be not related to any website).

If you add the Boolean, it will no longer be a traditional many:many mapping table. Probably FOREIGN KEYs will no longer work.
But you can certainly code for it -- however, you would need to have special code a la:
if flag is set, do ...
else do ...
That may turn into a UNION of two SELECTs. Or it may turn into something else. (I can't predict without understanding the use of this mapping table.)
Meanwhile, those partners that are related to no website can probably be handled by not being in the table at all. (This may need a 3rd if/then and/or UNION.)

You could consider a trigger on table B:
DELIMITER ;;
CREATE TRIGGER relate_all_b_to_a3
AFTER INSERT ON B
FOR EACH ROW
BEGIN
INSERT INTO (A_B)
VALUES ('A3', NEW.B)
ON DUPLICATE KEY UPDATE B=B;
END;;
DELIMITER ;

Well, if this thought is too weird - dont mind it - but you could roll it up the other way around. You could hava a A_HASNOT_B (a more intelligent name would be better) table. This way, new records in B will always be assigned to all records from A. You would simply have to invert a UX based user selection before storing relations.
So in your scenario, there would be the following records:
A | B
=======
A1 | B2
---+---
A2 | B3
Depending on your scenario/usecase, this could either be elegant or completely stupid. If you end up having only a few relations, this is a bad solution, in this case, i would go for reaanbs solution (trigger). If you think most records of type a will be assigned to type b it could do the job in a very simple way.

I would suggest a different approach from those of the other solutions. Looking at the problem from a more abstract point of view, you have two "subclasses" of A: those related with one or more B (let's call them A_relatable) and those with are always related to all B (let's call them A_related).
So I will suggest to represent your situation with five relations:
A(attributes of A)
B(attributes of B)
A_relatable (only primary key of A as foreign key, also primary key)
A_related (only primary key of A as foreign key, also primary key)
A_B(foreign key of A_relatable, foreign key of B)
In this way all the constraints can be verified, and you can define a few stored procedure to simplify the management of the data.

Related

A correct way to make relationships between three tables

I am building a library database and I am stuck on one particular thing.
I have three tables :BookCopy, BookLoan and Members. It is not clear to me how to make the relationships between them, so a member can borrow a book(or books) and all this to be correctly reflected in my database.
My idea was to have a two many-to-many tables, so I add BoakLoansMembers and BookCopiesBookLoans . I am not sure if this is correct, and even if it is, I have no idea how to scipt so many tables.
So, now I am wondering what would be the best thing to be done in this case and why?
I'm guessing your BookCopy is to account for having X copies of book Y, and in that sense "books" are not loaned, "copies" of them are, right?
I think the best course of action is probably to realize the BookLoan table should be the many-to-many table. A copy is loaned to a member at a time and then returned. BookLoad should have the id's for the copy and the member, and the date loaned (as you have now, though it should be a datetime field NOT a varchar one) & date returned (like the loaned date, it should be a datetime, but should also be nullable to represent unreturned copies). You should also keep the unique (presumably auto-increment) id of the loan as it is very possible a member might check out the same copy multiple times.
I am guessing that perhaps you were originally conceptualizing the "loan" similar to a sales transaction, which could work; but you would want a loanCopies table, and wouldn't want the dateReturned on the loan then since different copies could be returned independently.
Edit (additional observations):
isAvailable may be redundant if it is only based on whether the copy is checked out (if you want to withhold the book from circulation it might be appropriate though)
ISBN maxes at 13 characters according to wikipedia (char van be a little more efficient than varchar under some circumstances)
you might want to consider a languages table that the copy can reference rather than using a string type field.
Edit (re: isAvailable):
If you just need to find the copies not loaned out, a simple query like this is all you need.
SELECT *
FROM BookCopy
WHERE idBookCopy NOT IN (
SELECT idBookCopy
FROM BookLoan
WHERE dateReturned IS NULL
);
The subquery gets the list of copies loaned out, and the NOT IN makes sure the copies in the results are not in that list.
If you want to prevent a copy from being loaned out (damaged, vandalized, etc...) an isAvailable "flag" could be a simple way to add such functionality; just add AND isAvailable = 1 to the outer query's WHERE conditions.
You can just have an m:m relationship between Members and BookCopy and use your BookLoan Table as your cross join table. So you basically just have to add the references from the tables Members and Bookcopy to the Table BookLoan
BookLoan
---------------
idBookLoan
dateLoaned
dateReturned
idBookCopy FK -- add these two
idMember FK
And also consider making idBookCopy, idMember and dateLoaned the primary keys of your BookLoan Table

phpmyadmin foreign key problems

here's my situation:
I have this 3 tables:
Material(commercial_name,family,composition) (commercial_name is PK)
Chemical(commercial_name,CAS) (commercial_name is PK)
Compatibility_test(Chemical,Material,result) (Chemical and Material are foreign keys and refers to the PKs of the previous table).
EXAMPLE
I have these materials
M1,M2,M3
I have these chemicals
C1,C2,C3
Now no problem if i wanna instert a test of C3 on M2 or any other combinations with those elements. But if i do a test with a new C4 chemical on for example M2 , that is not allowed cause C4 is not in the Chemicals table yet.
The question is:
Is there a way to insert a test and if the elements of that test are not already in the Materials and Chemicals table they gonna be added , otherwise non changes to those table will be made?
Thank you in advance
The only way to do that by removing the relations between these tables. And already in like these cases 'by logic' you don't need these relations.
Relation to link to table with each other, when you delete 1 row you may delete the related rows or you forbidden deletion if there are related rows and so on. But in you case you don't need that.

Is it possible to reference a mysql table entry value from a second table entry dynamically?

I can't find anything about dynamically referencing one MySQL table entry to another. It may not be possible.
Essentially, I'd like to know if in MySQL you can do the equivalent to referencing the value of a certain Excel cell to another. For example, if in Excel I set Sheet 1 Cell A1 to some value like "MyVal". Then if I set Sheet 2 Cell A1 to "=Sheet1!A1" and Sheet 3 Cell A1 to "=Sheet2!A1" the value of Sheet 3 Cell A1 is "MyVal". If I go back to Sheet 1 Cell A1 and change the value to "MyNewVal" then the value is automatically updated on Sheet 2 Cell A1 and Sheet 3 Cell A1 to "MyNewVal".
My question is... in MySQL can I set the value of a certain entry in the first table to be dynamically linked to the value of a different entry in a second table such that when I query the first table (using existing PHP code) I get the value that's in the second table? I imagine that if it's possible then perhaps the value of the entry in the first table would look like a query that queries the second table for the correct value.
I understand how to write an UPDATE query in PHP to explicitly make the values the same but I don't want to change the existing php code. I want to link them in a relative/dynamic way. The short reason is that I don't want to change the PHP code since the same code is used on several of the sites I maintain and I want to keep the existing php code the same for cleaner maintenance/upgrading.
However, since the databases on the various sites are already different, it would be very clean to somehow dynamically link the appropriate entries in the different tables in the database itself.
Any help would be very appreciated. If this is possible, if you could just point me in the right direction, I'd be happy to do the research.
There are 2.5 ways to do this (basically two, but it feels like there's three):
From easiest to hardest...
Option 1:
If you need tableA to reflect tableB's value, don't store the value in tableA at all, just use tableB's value. Use either a join:
select a.*, b.col1
from tableA a
join tableB b on <some join condition>
or a subselect
select *, (select col1 from tableB where <some condition>) col1
from tableA
Option 2:
If you're happy with option 1, convert it to a view, which behaves like a table (except are restrictions on updating views that are joins):
create view myview as
select ... (one of the above selects)
Option 3:
Create a database trigger that fires when tableB's value is changed and copies the value over to the appropriate row/column in tableA
create trigger tableB_update
after update on tableB
for each row
update tableA set
tablea_col = new.col1
where id = new.tableA_id;
Note that new and old are special names given to the new and old rows so you can reference the values in the table being updated.
Choose the option that best suits your needs.
Databases don't really provide this type of facility, it's a completely different paradigm.
You can achieve the same results using joins, groupings or functions.
ALternatively if you wish to save the representation, store the query into a view which makes it more re-usable from various interfaces. More information on views can be found here; http://www.mysqltutorial.org/mysql-views-tutorial.aspx
Anything more complex and you will need to look at some business analysis tools.
Perhaps you have oversimplified the question, but you should not need to use a trigger. Just join the tables and any time 'MyVal' is changed it will automatically be available through query.
CREATE TABLE Sheet1
(
`ID` int auto_increment primary key
, `A` varchar(5)
)
;
INSERT INTO Sheet1
(`A`)
VALUES
('MyVal')
;
CREATE TABLE Sheet2
(
`ID` int auto_increment primary key
, `Sheet1FK` int)
;
INSERT INTO Sheet2
(`Sheet1FK`)
VALUES
(1)
;
CREATE TABLE Sheet3
(
`ID` int auto_increment primary key
, `Sheet2FK` int)
;
INSERT INTO Sheet3
(`Sheet2FK`)
VALUES
(1)
;
Query 1:
select
sheet3.id id3
, sheet2.id id2
, sheet1.id id1
, sheet1.a
from sheet1
inner join sheet2 on sheet1.id = sheet2.sheet1fk
inner join sheet3 on sheet2.id = sheet3.sheet2fk
Results:
| ID3 | ID2 | ID1 | A |
|-----|-----|-----|-------|
| 1 | 1 | 1 | MyVal |

SQL: Fastest Way to Dedupe to Canonical Ids

I have an interesting SQL task and though I would ask the community if anyone knows a fast way to accomplish it. I have 2 slow solutions, but I'm wondering if I am missing something faster.
Here is the task:
Given a list of records in a table, table A, with a column that references the primary key of another table, table B, logically speaking only though this is a MyISAM without foreign keys, we want to dedupe table B, and update table A to use the canonical deduped value from table B, and then delete all but the canonical id records from table B.
This might be easier illustrated via a small example. Lets say table A is a person table, and table B is a city table. Lets also say that there are records in the city table that are duplicates and need deduping. Lets say row 1 and row 2 of table B both refer to Los Angeles.
Then in the person table, we want to update all persons in Los Angeles with city id 2, to have city id 1, and delete the duplicate value from the city table with city id 2.
There may be many such rows representing the duplicated value, not just 2, you get the point. Right now, I am querying out all the cities from the city table, grouping them into equivaslence classes, looping over each equivalence class, nominating the canonical version in this case just choose the first, and performing 2 queries, the updated and the delete:
update person set city_id = $canonical_city_id where city_id in ($list_of_dupes)
Then
delete from city where city_id in ($list_of_dupes) and city_id != $canonical_city_id
I think there may be a faster way since we don't care which id is canonical, it could be the first, the in, or a random, doesn't matter. Can you think of a way to do this whole job in 1 SQL statement? What do you think is the fastest way?

Need suggestions to get a unique index keys for two or more tables on mysql

Usually, I use auto_increment attribute to get an unique key/id for each row on a single table to use as a primary index. Now I have two tables and unfortunately auto_increment can't create a new key using the last id/key on another table to keep all the keys/id unique in both. If I'll use auto_increment on both index columns, the possibility to have two identical ID is assured! There is a easy way to do it?
At least from a perspective of relational database design, your requirement seems unsound. You ought to create a separate table with the common auto_increment field and point to that from the other two tables with foreign keys.
If you have:
table A with ids 1,2,3.
table B with ids 1,2,3.
Then you already have the following (virtual) unique identifiers:
A1, A2, A3, B1, B2, B3
You don't need to make any changes to your database. This is purely a presentation issue.