Is it possible to merge two tables by primary key? - mysql

I have two tables, which I need to merge, and they are:
CREATE TABLE IF NOT EXISTS `legacy_bookmarks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`url` text,
`title` text,
`snippet` text,
`datetime` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `datetime` (`datetime`),
FULLTEXT KEY `title` (`title`,`snippet`)
)
And:
CREATE TABLE IF NOT EXISTS `legacy_links` (
`id` mediumint(11) NOT NULL AUTO_INCREMENT,
`user_id` mediumint(11) NOT NULL,
`bookmark_id` int(11) NOT NULL,
`status` enum('public','private') NOT NULL DEFAULT 'public',
UNIQUE KEY `id` (`id`),
KEY `bookmark_id` (`bookmark_id`)
)
As you can see, "legacy_links" contains the ID for "legacy_bookmarks". Am I able to merge the two, based on this relationship?
I can easily change the name of the ID column in "legacy_bookmarks" to "bookmark_id", if that makes things any easier.
Just so you know, the order of the columns, and their types, must be exact, because the data from this combined table is then to be imported into the new "bookmarks" table.
Also, I'd need to able to include additional columns (a "modification" column, populated with the "datetime" values), and change the order of the ones I have.
Any takers?

[Up to you to change the order of the columns]
CREATE TABLE `legacy_linkss` AS
SELECT l.id, l.url, l.title, l.snippet, l.datetime AS modification, b.user_id, b.status
FROM
`legacy_links` l
JOIN `legacy_bookmarks` b ON b.id = l.bookmark_id
;
Afterwards, after checking the consistency and adding manually the constraints, you may:
DROP TABLE `legacy_links`;
DROP TABLE `legacy_bookmarks`;
RENAME TABLE `legacy_linkss` TO `legacy_links`;

Yes, it's called a join, and you would do it like so:
SELECT *
FROM legacy_bookmarks lb
INNER JOIN legacy_links ll ON ll.bookmark_id = lb.id

Related

Multiple indexing in a table mysql

I have a table structure like this
`CREATE TABLE `like_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sender_id` int(11) NOT NULL,
`receiver_id` int(11) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `index_user` (`sender_id`,`receiver_id`))`
I have indexed both sender_id and receiver_id. If I try to query this
`Select * from like_user where sender_id = 10`
The index works fine but on the other way around it doesn't.
`Select * from like_user where receiver_id = 11`
How can I make the index work on both the conditions.
The use case is that sender_id is the one who is liking a user and the person who sender id is liking is stored in receiver_id. So If sender wants to see all the users he likes, then indexing works, but if the receiver_id wants to see which senders have liked him, indexing stops working. how we can resolve it?
Only prefix can be used. Postfix cannot. I think that two separate indices, one by sender and another by receiver, will be reasonable:
CREATE TABLE `like_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sender_id` int(11) NOT NULL,
`receiver_id` int(11) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY (`sender_id`),
KEY (`receiver_id`)
);
One of these indices will be used for each table copy. For example, for
SELECT *
FROM like_user t1
JOIN like_user t2 ON t1.sender_id = t2.receiver_id;
the first table copy (t1) will use KEY (`sender_id`) whereas another table copy will use KEY (`receiver_id`).

How to optimize a MySQL select with rows that do not have matching values in the other table

This question is more or less the same as this one: MySQL select rows that do not have matching column in other table; however, the solution there is not not practical for large data sets.
This table has ~120,000 rows.
CREATE TABLE `tblTimers` (
`TimerID` int(11) NOT NULL,
`TaskID` int(11) NOT NULL,
`UserID` int(11) NOT NULL,
`StartDateTime` datetime NOT NULL,
`dtStopTime` datetime NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `tblTimers`
ADD PRIMARY KEY (`TimerID`);
ALTER TABLE `tblTimers`
MODIFY `TimerID` int(11) NOT NULL AUTO_INCREMENT;
This table has about ~70,000 rows.
CREATE TABLE `tblWorkDays` (
`WorkDayID` int(11) NOT NULL,
`TaskID` int(11) NOT NULL,
`UserID` int(11) NOT NULL,
`WorkDayDate` date NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `tblWorkDays`
ADD PRIMARY KEY (`WorkDayID`);
ALTER TABLE `tblWorkDays`
MODIFY `WorkDayID` int(11) NOT NULL AUTO_INCREMENT;
tblWorkDays should have one line per TaskID per UserID per WorkDayDate, but due to a bug, a few work days are missing despite there being timers for those days; so, I am trying to create a report that shows any timer that does not have a work day associated with it.
SELECT A.TimerID FROM tblTimers A
LEFT JOIN tblWorkDays B ON A.TaskID = B.TaskID AND A.UserID = B.UserID AND DATE(A.StartDateTime) = B.WorkDayDate
WHERE B.WorkDayID IS NULL
Doing this causes the server to time out; so, I am looking for if there is a way to do this more efficiently?
You don't have any indexes on the columns you're joining on, so it has to do full scans of both tables. Try adding the following:
ALTER TABLE tblTimers ADD INDEX (TaskID, UserID);
ALTER TABLE tblWorkDays ADD INDEX (TaskID, UserID);

optimizing this query - which index or changes should i do?

Quite a simple question. But i'm a little bit lost when it come to sql optimization and index, i'm learning.
Query
SELECT A.*, count(A.ID) as count
FROM tableB B
JOIN tableA A ON A.ID = B.ID
WHERE B.otherID=xx and B.value='test' and B.languageID=3
Table A
CREATE TABLE `tableA` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`info1` varchar(64) NOT NULL default '',
`info2` varchar(64) NOT NULL default '',
PRIMARY KEY (`ID`)
) TYPE=MyISAM
Table B
CREATE TABLE `tableB` (
`ID` int(11) NOT NULL default '0',
`otherID` int(11) NOT NULL default '0',
`value` varchar(64) NOT NULL default '',
`languageID` int(11) NOT NULL default '0',
PRIMARY KEY (`ID`,`otherID`,`languageID`)
) TYPE=MyISAM
So the query is quite simple, i'm looking for the fields with a specific id and value in the table B, and i'm doing a join on table A because i need some infos which are in there.
I guess the query itself can't be optimized, but maybe i can speed up thing if i create an index, an index on (B.otherID,B.value) maybe ?
Thanks for you lights!
Normally the name ID is used for the PRIMARY KEY. A PRIMARY KEY is necessarily Unique. Yet you say
PRIMARY KEY (`ID`,`otherID`,`languageID`)
Is ID not unique, but this triple is? (Just checking.)
Back to your question...
WHERE B.otherID=xx and B.value='test' and B.languageID=3
Says that B needs those 3 columns in a composite index in any order. With that, the Optimizer will start with B, quickly find the row(s) needed there. Then it will move over to A, which already has an index on ID to handle ON A.ID = B.ID.
My Cookbook on creating indexes.
The normal pattern is COUNT(*). COUNT(x) has the extra burden of checking all the x values for being not NULL. (I suspect you did not need that.)
Use InnoDB, not MyISAM.

What is wrong with my JOINs?

So I am incredibly new to MySQL and I am struggling every step of the way to learning it. I am supposed to create a small database that can be about anything for my class assignment. I have the tables created but I am trying to figure out how to correctly JOIN them together.
Here is the code I currently have for it:
CREATE TABLE `monster` (
`monster_id` INTEGER NULL AUTO_INCREMENT DEFAULT NULL,
`name` VARCHAR(30) NULL DEFAULT NULL,
`hitpoints` TINYINT NULL DEFAULT NULL,
`armorclass` TINYINT NULL DEFAULT NULL,
PRIMARY KEY (`monster_id`)
);
CREATE TABLE `monster_size` (
`monster_id` INTEGER NULL AUTO_INCREMENT DEFAULT NULL,
`size_id` INTEGER NULL DEFAULT NULL,
PRIMARY KEY (`monster_id`)
);
CREATE TABLE `size` (
`size_id` INTEGER NULL AUTO_INCREMENT DEFAULT NULL,
`size` VARCHAR(15) NULL DEFAULT NULL,
PRIMARY KEY (`size_id`)
);
ALTER TABLE `monster_size` ADD FOREIGN KEY (monster_id) REFERENCES `monster` (`monster_id`);
ALTER TABLE `monster_size` ADD FOREIGN KEY (size_id) REFERENCES `size` (`size_id`);
/* Here is where I am trying to get the joining to happen */
SELECT name,hitpoints,size
FROM monster m
JOIN size s ON (m.monster_id=s.size_id)
JOIN monster_size ms ON (s.size_id=ms.size_id);
You're not joining the tables in the correct way. You'll want to join the monster table with the monster_size junction table, and then the size table with the monster_size table, like this:
SELECT name,hitpoints,size
FROM monster m
JOIN monster_size ms ON m.monster_id = ms.monster_id
JOIN size s ON ms.size_id = s.size_id
In your current query you're trying to match the id from the size table with the id from the monster table, but those two tables doesn't have any direct relation, so you need to use the monster_size table to connect them.
You are using a linking table monster_size. This means that you are creating an m to n relation. Can a monster really have several sizes at the same time? Because now, a size can be linked to several monsters, but a monster can also be linked to several sizes through this linking table.
Also this linking table should not have an auto increment column.
But probably you just want to link one size to a monster at a time. Forget the monster_size table. Instead add a size_id column to the monster table (no auto increment). Probably the set of possible sizes will not change anyway, so you could drop the auto increment in the size table as well and add well defines IDs "manually".
New table structure
DROP TABLE IF EXISTS `monster`;
CREATE TABLE `monster` (
`monster_id` INTEGER NULL AUTO_INCREMENT DEFAULT NULL,
`name` VARCHAR(30) NULL DEFAULT NULL,
`hitpoints` TINYINT NULL DEFAULT NULL,
`armorclass` TINYINT NULL DEFAULT NULL,
`size_id` INTEGER NULL DEFAULT NULL,
PRIMARY KEY (`monster_id`)
);
DROP TABLE IF EXISTS `size`;
CREATE TABLE `size` (
`size_id` INTEGER NULL DEFAULT NULL,
`size` VARCHAR(15) NULL DEFAULT NULL,
PRIMARY KEY (`size_id`)
);
ALTER TABLE `monster` ADD FOREIGN KEY (size_id) REFERENCES `size` (`size_id`);
INSERT INTO size (size_id, size) VALUES (1, 'small');
INSERT INTO size (size_id, size) VALUES (2, 'big');
INSERT INTO size (size_id, size) VALUES (3, 'huge');
INSERT INTO monster(name, hitpoints, size_id) VALUES ('Tester', 10, 1);
Now, your query goes like this
SELECT m.name, m.hitpoints, s.size
FROM
monster m
LEFT JOIN size s
ON (m.size_id = s.size_id);
Also use a left outer join, in case a size_id is null in the monster table.

MySQL Query Optimization for large tables

I have a query that take 50 seconds
SELECT `security_tasks`.`itemid` AS `itemid`
FROM `security_tasks`
INNER JOIN `relations` ON (`relations`.`user_id` = `security_tasks`.`user_id` AND `relations`.`relation_type_id` = `security_tasks`.`relation_type_id` AND `relations`.`relation_with` = 3001 )
Records in security_tasks = 841321 || Records in relations = 234254
CREATE TABLE `security_tasks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`itemid` int(11) DEFAULT NULL,
`relation_type_id` int(11) DEFAULT NULL,
`Task_id` int(2) DEFAULT '0',
`job_id` int(2) DEFAULT '0',
`task_type_id` int(2) DEFAULT '0',
`name` int(2) DEFAULT '0'
PRIMARY KEY (`id`),
KEY `itemid` (`itemid`),
KEY `relation_type_id` (`relation_type_id`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1822995 DEFAULT CHARSET=utf8;
CREATE TABLE `relations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`relation_with` int(11) DEFAULT NULL,
`relation_type_id` int(11) DEFAULT NULL,
`manager_level` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `relation_with` (`relation_with`),
KEY `relation_type_id` (`relation_type_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1082882 DEFAULT CHARSET=utf8;
what can i do to make it fast, like 1 or 2 seconds fast
EXPLAIN :
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE relations ref user_id,relation_with,relation_type_id relation_with 5 const 169 Using where
1 SIMPLE security_tasks ref relation_type_id,user_id user_id 5 transparent.relations.user_id 569 Using where
UPDATE :
adding a composite key minimized the time to 20 seconds
ALTER TABLE security_tasks ADD INDEX (user_id, relation_type_id) ; ALTER TABLE relations ADD INDEX (user_id, relation_type_id) ; ALTER TABLE relations ADD INDEX (relation_with) ;
The problem is when the relations table has large data for the selected user (relations.relation_with` = 3001 )
any ideas ?
Adjust your compound index slightly, don't do just two, but all three parts
ALTER TABLE relations ADD INDEX (user_id, relation_type_id, relation_with)
The index does not just have to be on the joined columns, but SHOULD be based on joined columns PLUS anything else that makes sense as querying criteria is concerned (within reason, takes time to learn more efficiencies). So, in the case suggested, you know the join on the user and type, but are also specific to the relation with... so that is added to the same index.
Additionally, your security task table, you could add the itemID to the index to make it a covering index (ie: covers the join conditions AND the data element(s) you want to retrieve). This too is a technique, and should NOT include all other elements in a query, but since this is a single column might make sense for your scenario. So, look into "covering indexes", but in essence, a covering index qualifies the join, but since it also has this "itemid", the engine does not have to go back to the raw data pages of the entire security tasks table to get that one column. It's part of the index so it grabs whatever qualified the join and comes along for the ride and you are done.
ALTER TABLE security_tasks ADD INDEX (user_id, relation_type_id, itemid) ;
And for readability purposes, especially with long table names, it's good to use aliases
SELECT
st.itemid
FROM
security_tasks st
INNER JOIN relations r
ON st.user_id = r.user_id
AND st.relation_type_id = r.relation_type_id
AND r.relation_with = 3001