MySQL inserting rows with 0 value in primary key - mysql

MySQL (v5.41) on Ubuntu is inserting rows with primary key value as 0.
Below is the MySQL table data.
mysql> select id from keywords where text_id = 72;
+----+
| id |
+----+
| 0 |
| 0 |
+----+
| keywords | CREATE TABLE `keywords` (
`id` int(11) NOT NULL DEFAULT '0',
`to_user_id` bigint(20) DEFAULT NULL,
`text_id` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 |
+--------+---------------------------------

set the primary key column to AUTO INCREMENT also.
Like
CREATE TABLE table (
id INT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM;

You probably don't have the field id as AUTOINCREMENT

Related

Row Constructor Expression selects not using index

we have this table
CREATE TABLE `resource_grant` (
`resource_grant_id` int(11) NOT NULL AUTO_INCREMENT,
`member_type_id` int(11) NOT NULL,
`member_ref` varchar(36) NOT NULL,
`resource_type_id` int(11) NOT NULL,
`resource_ref` varchar(36) NOT NULL,
`role_id` int(11) NOT NULL,
`modified_by` varchar(255) NOT NULL,
`modified_timestamp` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`deleted` tinyint(1) NOT NULL DEFAULT '0',
`parent_grant_id` int(11) DEFAULT NULL,
PRIMARY KEY (`resource_grant_id`),
UNIQUE KEY `member_ref` (`member_ref`,`member_type_id`,`resource_type_id`,`resource_ref`),
KEY `member_type_id` (`member_type_id`),
KEY `resource_type_id` (`resource_type_id`),
KEY `role_id` (`role_id`),
KEY `resource_ref` (`resource_ref`,`resource_type_id`),
KEY `idx_rg_parent_grant_id` (`parent_grant_id`),
KEY `resource_ref_2` (`resource_ref`,`member_ref`,`resource_type_id`,`member_type_id`,`role_id`),
CONSTRAINT `resource_grant_ibfk_1` FOREIGN KEY (`member_type_id`) REFERENCES `member_type` (`member_type_id`),
CONSTRAINT `resource_grant_ibfk_2` FOREIGN KEY (`resource_type_id`) REFERENCES `resource_type` (`resource_type_id`),
CONSTRAINT `resource_grant_ibfk_3` FOREIGN KEY (`role_id`) REFERENCES `role` (`role_id`)
) ENGINE=InnoDB;
and these related tables
CREATE TABLE `member_type` (
`member_type_id` int(11) NOT NULL AUTO_INCREMENT,
`member_type` varchar(36) NOT NULL,
`modified_by` varchar(36) NOT NULL,
`modified_timestamp` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`deleted` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`member_type_id`),
UNIQUE KEY `member_type` (`member_type`),
KEY `member_type_2` (`member_type`)
) ENGINE=InnoDB;
CREATE TABLE `resource_type` (
`resource_type_id` int(11) NOT NULL AUTO_INCREMENT,
`resource_type` varchar(36) NOT NULL,
`modified_by` varchar(36) NOT NULL,
`modified_timestamp` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`deleted` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`resource_type_id`),
UNIQUE KEY `resource_type` (`resource_type`),
KEY `resource_type_2` (`resource_type`)
) ENGINE=InnoDB;
CREATE TABLE `role` (
`role_id` int(11) NOT NULL AUTO_INCREMENT,
`role_ref` varchar(50) NOT NULL,
`name` varchar(256) NOT NULL,
`modified_by` varchar(36) NOT NULL,
`modified_timestamp` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`deleted` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`role_id`),
UNIQUE KEY `role_ref` (`role_ref`),
KEY `role_ref_2` (`role_ref`)
) ENGINE=InnoDB;
and we need to run selects like these ("Row Constructor Expression" syntax) (basically "bulk selects")
SELECT rg.resource_grant_id
FROM resource_grant rg
JOIN resource_type rt ON rg.resource_type_id = rt.resource_type_id
JOIN member_type mt ON rg.member_type_id = mt.member_type_id
JOIN role r ON r.role_id = rg.role_id
WHERE
(rg.resource_ref, rg.member_ref, rt.resource_type, mt.member_type, r.role_ref)
IN
(
('759','624962','property','epc-user','role.171'),
('11974','624962','property','epc-user','role.171')
);
the selects take ~60s to run, which is unacceptably long
note that there IS an index for (resource_ref,member_ref,resource_type_id,member_type_id,role_id)
we also don't want to run n individual select statements - we need these "bulk selects".
mysql 5.6 docs talk about this style of select not using indexes but you can make it using some tricks
https://dev.mysql.com/doc/refman/5.6/en/row-constructor-optimization.html
https://dev.mysql.com/doc/refman/5.6/en/range-optimization.html
not sure what's missing for us in order to make it use the indexes
EDIT here's the plan
mysql> explain SELECT rg.resource_grant_id FROM resource_grant rg JOIN resource_type rt ON rg.resource_type_id = rt.resource_type_id JOIN member_type mt ON rg.member_type_id = mt.member_type_id JOIN role r ON r.role_id = rg.role_id WHERE (rg.resource_ref, rg.member_ref, rt.resource_type, mt.member_type, r.role_ref) IN ( ('759','624962','property','epc-user','role.171'), ('11974','624962','property','epc-user','role.171') );
+----+-------------+-------+--------+-----------------------------------------+----------------+---------+--------------------------+---------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-----------------------------------------+----------------+---------+--------------------------+---------+----------------------------------------------------+
| 1 | SIMPLE | rt | index | PRIMARY | resource_type | 38 | NULL | 3 | Using index |
| 1 | SIMPLE | mt | index | PRIMARY | member_type | 38 | NULL | 6 | Using index; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | rg | ref | member_type_id,resource_type_id,role_id | member_type_id | 4 | samsDB.mt.member_type_id | 2370237 | Using where |
| 1 | SIMPLE | r | eq_ref | PRIMARY | PRIMARY | 4 | samsDB.rg.role_id | 1 | Using where |
+----+-------------+-------+--------+-----------------------------------------+----------------+---------+--------------------------+---------+----------------------------------------------------+
4 rows in set (0.53 sec)
Start by changing the where clause to:
WHERE rg.member_ref = '624962' AND
rt.resource_type = 'property' AND
mt.member_type = 'epc-user' AND
r.role_ref = 'role.171' AND
rg.resource_ref IN ('759', '11974')
The existing indexes are not quite optimal for this. You need an index where the first two keys are (member_ref, resource_ref) -- well, except in the most recent versions of MySQL which implement skip-scan index optimizations.
You might be able to change resource_ref_2 to:
KEY `resource_ref_2` (`member_ref`, `resource_ref`, `resource_type_id`, `member_type_id`, `role_id`),
I'm not surprised at 60s on 5.6. "Row constructors" have existed for a long time. But they were not optimized before 5.7.
Either upgrade or rewrite the WHERE as Gordon suggests.

Modify child record (update modified_date and is_delete) when parents' respective values are changed

My question is in reference to this question.
Say I have three tables, user, country and user_activity whose schema/table-structure are given below:-
|---------------------------------------------|
| id | fname | lname | status |
|---------------------------------------------|
country:-
|--------------------------------|
| id | name | status |
|--------------------------------|
user_activity
|-------------------------------------------------------------------------|
| id | user_id | activity_type | country_id | location | status |
|-------------------------------------------------------------------------|
Say, I create the user table like this:-
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`fname` varchar(255) NOT NULL,
`lname` varchar(255) NOT NULL,
`status` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `iduser` (`id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
Then, I create the country table like this:-
CREATE TABLE `country` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`status` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `idcountry` (`id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
I want to create a table user_activity where the status of user_activity record would change according to the status of user if user_activity.user_id matches user.id
At the same time, if status of user_activity record would change according to the status of country if user_activity.country_id matches country.id.
How can I do that? How can I achieve the objective at the database level, instead of setting it via PHP scripting?

Generating auto incrementing numbers with two columns

I want to create a table so that I have an ID number that is based on the date, AND an ID as a unique, primary key.
Ie:
2015-2-1-1
2015-2-1-2
but, if I create:
2015-2-2-1
the counter should restart at 1.
I've tried using the following:
CREATE TABLE `invoices` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`date` date NOT NULL,
`po_id` int(11) unsigned DEFAULT NULL,
`description` varchar(256) NOT NULL,
`client_id` int(11) unsigned DEFAULT NULL,
`status` enum('unpaid','paid','partial') DEFAULT 'unpaid',
PRIMARY KEY (`id`,`date`),
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=latin1;
But it doesn't work like I want.
If you want a two column primary key (date, id) and id that should auto increment starting from 1 each date. Just set date, for your primary column key, and id in second position in your key definition.
CREATE TABLE `invoices` (
`date` date NOT NULL,
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`po_id` int(11) unsigned DEFAULT NULL,
`description` varchar(256) NOT NULL,
`client_id` int(11) unsigned DEFAULT NULL,
`status` enum('unpaid','paid','partial') DEFAULT 'unpaid',
PRIMARY KEY (`date`, `id`)
) ENGINE=MyIsam DEFAULT CHARSET=latin1;
The internal key will be set as your example, but you will see two columns:
+----+------------+
| id | date |
+----+------------+
| 1 | 2012-01-01 |
| 2 | 2012-01-01 |
| 1 | 2012-01-02 |
| 2 | 2012-01-02 |
+----+------------+
EDIT: You have to use MyIsam format

Altering existing unique constraint

I have a table that was defined like this:
CREATE TABLE `Message` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`user_id` integer NOT NULL,
`user_to` integer NOT NULL,
`top_num` integer NOT NULL,
`priority` smallint NOT NULL,
`error` varchar(120) NOT NULL,
UNIQUE (`user_id`, `user_to`, `top_num`)
);
Later, I added another column to it, msg_type, like this:
ALTER TABLE Message ADD COLUMN msg_type SMALLINT(6) NOT NULL DEFAULT 0;
However, I have come to realize that I need to change my original UNIQUE constraint to include msg_type. I tried running
ALTER TABLE Message
ADD UNIQUE INDEX (`user_id`, `user_to`, `top_num`, `msg_type`);
but INSERTs into my table still fail, and the error message indicates that that is because the old uniqueness constraint fails.
When I call describe Messages in mysql I see the following:
+-----------------+----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+----------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | NO | MUL | NULL | |
| user_to | int(11) | NO | MUL | NULL | |
| top_num | int(11) | NO | MUL | NULL | |
| priority | smallint(6) | NO | | NULL | |
| error | varchar(120) | NO | | NULL | |
| msg_type | smallint(6) | NO | | 0 | |
+-----------------+----------------------+------+-----+---------+----------------+
which makes it seem like msg_type really isn't part of the constraint... How can I alter the constraint that the table was defined with, short of recreating the table?
As in previous answer to change foreign key constraint use steps:
Step 1: Drop old constraint:
ALTER TABLE `Message` DROP INDEX `user_id`;
Step 2: Add new:
ALTER TABLE `Message` ADD UNIQUE INDEX (
`user_id`,
`user_to`,
`top_num`,
`msg_type`);
Use SHOW CREATE TABLE to know name of constraint:
mysql> SHOW CREATE TABLE `Message` ;
| Message | CREATE TABLE `Message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`user_to` int(11) NOT NULL,
`top_num` int(11) NOT NULL,
`priority` smallint(6) NOT NULL,
`error` varchar(120) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`,`user_to`,`top_num`)
-- ^^^^^^^^^ name
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
If you checks:
mysql> SHOW INDEX FROM `Message`;
Key_name is user_id that is first argument in UNIQUE (user_id ....
Suppose if you write:
ALTER TABLE `Message` ADD UNIQUE INDEX (
`user_to`,
`user_id`,
`top_num`,
`msg_type`);
Then you have to drop using user_to as:
ALTER TABLE `Message` DROP INDEX `user_to`;
This is because you are adding unique index. Please first drop unique index and then add unique constraint.
-- you have to drop each index one by one.
ALTER TABLE Message DROP UNIQUE INDEX user_id;
and now add unique constraint.
ALTER TABLE Message ADD CONSTRAINT uc_message UNIQUE (`user_id`, `user_to`, `top_num`, `msg_type`);
This is because you haven't deleted the first unique constraint you have created. Right now, you have two unique constraints on your table.
To delete a unique constraint, have a look at this post
Dropping Unique constraint from MySQL table
This is because you are adding unique index. Please first drop unique index and then add unique constraint.
DROP INDEX index_name ON table_name
and now add unique constraint.
ALTER TABLE Message
ADD CONSTRAINT uc_message UNIQUE ((`user_id`, `user_to`, `top_num`, `msg_type`);)

Why is this simple query taking a long time to execute and performing full table scan?

simple query is taking long time around 630 sec to execute
performing full table scan.
please help me to rewrite the query and also suggest me if any indexes need to add.
Query:
mysql> explain SELECT count(DISTINCT(tab1.idnum)) as totalresults FROM (`tab1`) LEFT JOIN `tab2` ON tab2.idnum = tab1.col1id WHERE tab1.userid = '165258' AND `result` = 'correct' AND tab2.department = 'DEPT1' AND tab2.book = 2096 AND `quarantined` = 0;
Explain Plan:
+----+-------------+--------------+--------+------------------------------------+---------+---------+---------------------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+--------+------------------------------------+---------+---------+---------------------------+-------+-------------+
| 1 | SIMPLE | tab1 | ref | userid,col1id,result,userid_status | userid | 4 | const | 14720 | Using where |
| 1 | SIMPLE | tab2 | eq_ref | PRIMARY | PRIMARY | 4 | comp1.tab1.col1id | 1 | Using where |
+----+-------------+--------------+--------+------------------------------------+---------+---------+---------------------------+-------+-------------+
2 rows in set (0.00 sec)
Table structutre:
mysql> show create table tab1\G
*************************** 1. row ***************************
Table: tab1
Create Table: CREATE TABLE `tab1` (
`idnum` int(11) NOT NULL AUTO_INCREMENT,
`questid` int(11) NOT NULL DEFAULT '0',
`userid` int(11) NOT NULL DEFAULT '0',
`col1id` int(11) NOT NULL DEFAULT '0',
`result` enum('correct','incorrect') NOT NULL DEFAULT 'def1',
`answergiven` varchar(35) NOT NULL DEFAULT '0',
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`status` enum('calibrating','normal') NOT NULL DEFAULT 'def2',
`quarantined` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`idnum`),
KEY `questid` (`questid`),
KEY `userid` (`userid`),
KEY `col1id` (`col1id`),
KEY `result` (`result`),
KEY `userid_status` (`userid`,`status`),
KEY `questid_status` (`questid`,`status`),
KEY `timestamp` (`timestamp`)
) ENGINE=InnoDB AUTO_INCREMENT=143018786 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql> show create table tab2\G
*************************** 1. row ***************************
Table: tab2
Create Table: CREATE TABLE `tab2` (
`idnum` int(11) NOT NULL AUTO_INCREMENT,
`userid` int(11) NOT NULL DEFAULT '0',
`timestarted` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`timefinished` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`questionlist` mediumtext NOT NULL,
`topics` mediumtext NOT NULL,
`totalnum` int(11) NOT NULL DEFAULT '0',
`completednum` int(11) NOT NULL DEFAULT '0',
`assignment` int(11) NOT NULL DEFAULT '0',
`department` varchar(255) NOT NULL DEFAULT '',
`book` int(11) NOT NULL DEFAULT '0',
`cqs` mediumtext NOT NULL,
`metatype` varchar(25) DEFAULT 'topic',
PRIMARY KEY (`idnum`),
KEY `userid` (`userid`),
KEY `assignment` (`assignment`)
) ENGINE=InnoDB AUTO_INCREMENT=13547403 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
You need a multi-column index, ie. one that spans userid AND col1id in tab1 at the same time. Try:
create index idx_usr_col1 on tab1(userid,col1id)
Try adding multi-column indices for MySQL to use:
ALTER TABLE `tab2` ADD INDEX idx_col1id_userid_result_quarantined (`col1id`, `userid`, `result`, `quarantined`);
ALTER TABLE `tab1` ADD INDEX idx_department_book (`department`, `book`);
Try this:
SELECT count(distinct result.idnum)) as totalresults FROM `tab1` as result
inner join (select distinct idnum from `tab2` where department = 'DEPT1' and book = 2096 ) as col11 ON col11.idnum = result.col1id
WHERE result.userid = '165258' AND `result` = 'correct' AND `quarantined` = 0;