I am working on the design of a database that will be used to get an unique ID. The "main" table will have a composite primary key by getting the id from others tables
As an example, consider the following table that illustrates the problem:
----------------------------------------------------------------
| PK_main_table| FK1 | FK2 | ... |
----------------------------------------------------------------
| 102_245 | 102 | 245 | ... |
| 102_984 | 102 | 984 | ... |
| 98_298 | 98 | 298 | ... |
| 564_114 | 564 | 114 | ... |
----------------------------------------------------------------
So I will wrote the following :
CREATE TABLE main_table
(
`id` INT NOT NULL AUTO_INCREMENT,
FK1 int(5),
FK2 int(5),
...
primary key(`FK1`, `_`, `FK2`, ...)
);
Note that all the foreign keys have an unique ID and it is possible for the same foreign key to be found many time (as you can see with FK1='102').
I have a decent understanding of relational databases, but am far from an expert or even an experienced user.
My question is : How can I get the primary key ? I wish I could do a SELECT [something there] where FK1='98' and get '98_298' as a result.
My solution was to add a new field "primary_key" add fill in with a trigger with the following code :
select
id,group_concat(concat(`FK1`,':',`FK2`) separator ',')
as Result from main_table group by id
Then I just have to
select main_table.primary_key where [my condition]
I do not know if it is the best solution but it works great like that.
Related
I know such a question is asked before. I made sure that they have the same data type and also checked my syntax, but I am still getting the error:
ALTER TABLE meetings ADD FOREIGN KEY (ownerName) REFERENCES employees(name);
ERROR 1215 (HY000): Cannot add foreign key constraint
mysql> desc `meetings`;
+-----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| room | int(6) | NO | | NULL | |
| ownerName | varchar(30) | NO | | NULL | |
| ownerID | varchar(30) | NO | | NULL | |
+-----------+-------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
mysql> desc `employees`;
+----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| name | varchar(30) | NO | | NULL | |
| username | varchar(30) | NO | PRI | NULL | |
| pswd | varchar(255) | YES | | NULL | |
+----------+--------------+------+-----+---------+-------+
What am I doing wrong?
name is not primary key in employees table so .. try using username
ALTER TABLE meetings ADD FOREIGN KEY (ownerName) REFERENCES employees(username);
or as suggested by DanielE or you can use the column name but need an UNIQUE index for this column
Change the primary key of 'employees' from user name to name. Then you can use
ALTER TABLE meetings ADD FOREIGN KEY (ownerName) REFERENCES employees(name);
What am I doing wrong? Error 1215 is probably the least of the problems here.
Some of the answers given for this question suggest making username the referenced column in employees rather than name, which is fine as far as it goes, but ignores some really fundamental issues in the schema and quite possibly wasn't the poster's intention for these columns. The rest of this answer is based on my own set of assumptions.
meetings table
Looking at the meetings table, I'm left wondering about the purpose of the ownerID column. Since the intention is to have ownerName as a foreign key to employees, what exactly is ownerID? The name suggests it also somehow references employees, but there is no id or ownerID in employees. Also, if any column starting owner... refers to an employee then why would you need both in the meetings table? One of them is surely redundant. Why is ownerID a VARCHAR(30)? ID columns tend to be INT. Of course, I may be reading to much into this and ownerID may have some other purpose that has nothing to do with an employee, but if that's the case the name is likely going to cause confusion in the future.
The meetings table also has an INT surrogate key in id. There's another INT for room. Since room isn't a foreign key, it suggests that rooms are either consistently identified only by number (which would be strange in my experience) and that there is nothing more to a 'room' that's worth capturing (e.g. location, capacity, equipment etc.) to bother with modelling data about the room in a separate table (again unlikely). Alternatively, room might itself be a foreign key referencing an INT id column in an, as yet undefined, rooms table.
employees table
If we accept ownerID as a more appropriate foreign key to the employee that owns the meeting (it uses less memory to index than either name or username) then consistency would suggest another surrogate key id as the primary key in the employees table. It's not necessary to do this, username would be unique and is fine on it's own, but it's simpler and more efficient. The other suggestion made that name should be the PK in employees is wrong - it presupposes that names are always unique.
A single column to cover an employee name would also be unusual.
The point made about referencing a PK or a unique index is well made (even if it's not strictly necessary in Innodb), I'd just say that ownerName is the wrong foreign key and username and name are the wrong references because there is a better alternative.
And, finally, is a NULL password (pswd) a good idea?
In a database, I have a username table and I have jurisdiction table. I also have a intermediate table to assign a user to one or more jurisdictions.
employee table
userID (primary Key)
firstName
lastName
records:
+--------+-----------+----------+
| userID | firstName | lastName |
+--------+-----------+----------+
| 6 | John | Doe |
| 11 | lisa | lopez |
+--------+-----------+----------+
jurisdictions table
jurId (Primary Key)
region
records:
+-------+--------------+
| jurID | jurisdiction |
+-------+--------------+
| 1 | California |
| 2 | Texas |
| 3 | Washington |
| 4 | South Dakota |
| 5 | Alaska |
| 6 | Ohio |
+-------+--------------+
user_jurisdiction
userID (Foriegn Key pointing to employees userID)
jurID (Foriegn Key pointing to jurisdictions jurID)
records:
+--------+-------+
| userID | jurID |
+--------+-------+
| 6 | 2 |
| 6 | 3 |
| 11 | 2 |
+--------+-------+
I want to make it where if I delete a parent table row, the intermediate table with the corresponding foreign key would be deleted automatically. I made the intermedieate table with:
CREATE TABLE user_jurisdiction(userID int NOT NULL, jurID int NOT NULL, FOREIGN KEY(userID) REFERENCES employee(userID) ON DELETE CASCADE, FOREIGN KEY (jurID) REFERENCES jurisdictions(jurID) ON DELETE CASCADE);
But when I delete something from the parent table....like a state name row from table jurisidictions.jurisdiction...the userID and jurID row does not get deleted from the intermediate table user_jurisdiction automatically. From what I can see, I made the DELETE ON CASCADE correct on each foreign key. Why aren't the corresponding rows with the foreign keys deleting in the intermediate table when the parent table row get's deleted?
The most likely reason is that you're using the MyISAM engine instead of the INNODB engine. The MyISAM engine parses foreign key constraints, but it doesn't enforce them.
CREATE TABLE user_jurisdiction(
userID int NOT NULL,
jurID int NOT NULL,
FOREIGN KEY(userID)
REFERENCES employee(userID) ON DELETE CASCADE,
FOREIGN KEY (jurID)
REFERENCES jurisdictions(jurID) ON DELETE CASCADE
) ENGINE=INNODB;
INNODB is the default storage engine for MySQL starting with version 5.5. MyISAM was the default before that.
Get in the habit of posting SQL DDL instead of (or along with) a description of the tables. DDL is a lot more accurate than a description.
I have two tables:
CREATE TABLE IF NOT EXISTS treaties(
id INT NOT NULL AUTO_INCREMENT,
name varchar(50) NOT NULL,
PRIMARY KEY(id)
)ENGINE=InnoDB;
and
CREATE TABLE IF NOT EXISTS items(
id INT NOT NULL AUTO_INCREMENT,
treaty INT NOT NULL,
item varchar(20),
PRIMARY KEY(id),
FOREIGN KEY (treaty) REFERENCES treaties(id)
ON UPDATE RESTRICT
ON DELETE RESTRICT
)ENGINE=InnoDB;
After that I inserted few lines in each of tables but values treaties.id and items.treaty were the same.
When I run
EXPLAIN SELECT *
FROM `items`
JOIN `treaties` ON `items`.`treaty` = `treaties`.`id`
WHERE 1
I obtained:
id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
1 | SIMPLE | treaties| ALL | PRIMARY | NULL| NULL | NULL| 3 |
1 | SIMPLE | items | ALL | treaty | NULL| NULL | NULL| 4 | Using where; Using join buffer
I thought if I have foreign key between items.treaty and treaties.id this key must used and type must not be ALL.
What is wrong?
Please, help me!
Thank you!
As explained in the manual:
The output from EXPLAIN shows ALL in the type column when MySQL uses a table scan to resolve a query. This usually happens under the following conditions:
[...]
The table is so small that it is faster to perform a table scan than to bother with a key lookup. This is common for tables with fewer than 10 rows and a short row length. Don't worry in this case.
I've been messing around all day trying to find why my query performance is terrible. It is extremely simple, yet can take over 15 minutes to execute (I abort the query at that stage). I am joining a table with over 2 million records.
This is the select:
SELECT
audit.MessageID, alerts.AlertCount
FROM
audit
LEFT JOIN (
SELECT MessageID, COUNT(ID) AS 'AlertCount'
FROM alerts
GROUP BY MessageID
) AS alerts ON alerts.MessageID = audit.MessageID
This is the EXPLAIN
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
| 1 | PRIMARY | AL | index | NULL | IDX_audit_MessageID | 4 | NULL | 2330944 | 100.00 | Using index |
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 124140 | 100.00 | |
| 2 | DERIVED | alerts | index | NULL | IDX_alerts_MessageID | 5 | NULL | 124675 | 100.00 | Using index |
This is the schema:
# Not joining, just showing types
CREATE TABLE messages (
ID int NOT NULL AUTO_INCREMENT,
MessageID varchar(255) NOT NULL,
PRIMARY KEY (ID),
INDEX IDX_messages_MessageID (MessageID)
);
# 2,324,931 records
CREATE TABLE audit (
ID int NOT NULL AUTO_INCREMENT,
MessageID int NOT NULL,
LogTimestamp timestamp NOT NULL,
PRIMARY KEY (ID),
INDEX IDX_audit_MessageID (MessageID),
CONSTRAINT FK_audit_MessageID FOREIGN KEY(MessageID) REFERENCES messages(ID)
);
# 124,140
CREATE TABLE alerts (
ID int NOT NULL AUTO_INCREMENT,
AlertLevel int NOT NULL,
Text nvarchar(4096) DEFAULT NULL,
MessageID int DEFAULT 0,
PRIMARY KEY (ID),
INDEX IDX_alert_MessageID (MessageID),
CONSTRAINT FK_alert_MessageID FOREIGN KEY(MessageID) REFERENCES messages(ID)
);
A few very important things to note - the MessageID is not 1:1 in either 'audit' or 'alerts'; The MessageID can exist in one table, but not the other, or may exist in both (which is the purpose of my join); In my test DB, none of the MessageID exist in both. In other words, my query will return 2.3 million records with 0 as the count.
Another thing to note is that the 'audit' and 'alert' tables used to use MessageID as varchar(255). I created the 'messages' table expecting that it would fix the join. It actually made it worse. Previously, it would take 78 seconds, now, it never returns.
What am I missing about MySQL?
Subqueries are very hard for the MySQL engine to optimize. Try:
SELECT
audit.MessageID, COUNT(alerts.ID) AS AlertCount
FROM
audit
LEFT JOIN alerts ON alerts.MessageID = audit.MessageID
GROUP BY audit.MessageID
You're joining to a subquery.
The subquery results are effectively a temporary table - note the <derived2> in the query execution plan. As you can see there, they're not indexed, since they're ephemeral.
You should execute the query as a single unit with a join, rather than joining to the results of a second query.
EDIT: Andrew has posted an answer with one example of how to do your work in a normal join query, instead of in two steps.
In general, we can use unique key or primary key to prevent this, but in my case. I am creating an MyFavorite table like this:
---------------------------------------------------------
| UserName | FavoriteLink |
---------------------------------------------------------
| Ryan | http://www.google.com |
---------------------------------------------------------
| Ryan | http://www.yahoo.com |
---------------------------------------------------------
| Joyce | http://www.google.com |
---------------------------------------------------------
| Joyce | http://www.cnn.com |
---------------------------------------------------------
So, each user can have a lot of favoritelinks, but they shouldn't have duplicate favoritelink, for example, Ryan shouldn't have two favoritelink for http://www.google.com. but for this table, FavoriteLink field may be duplicate, because both Ryan and Joyce, they all have favoritelink for http://www.google.com.
Here is the question: how can I insert data into this table without duplicate FavoriteLink for specific person?
Composite keys.
CREATE TABLE userlinks (
user VARCHAR(255),
link VARCHAR(255),
PRIMARY KEY (user, link)
)
or
CREATE TABLE userlinks (
id INTEGER AUTO_INCREMENT PRIMARY KEY,
user VARCHAR(255),
link VARCHAR(255),
UNIQUE KEY (user, link)
)
depending on what exactly it is you want.
You can add a composite unique index.
CREATE UNIQUE INDEX username_favorite_uniq ON yourTable (UserName, FavoriteLink)