Solving error "Expression of generated column 'registrationno' contains a disallowed function" - mysql

Can someone tell me the right way of doing an generated column in mysql.
im supposed to generate a registration_no in format:
SVSRYYYYinvoice_no
yyyy-> year
I have been using MYSQL workbench to create my db but i'm getting this error
ERROR 3102: Expression of generated column 'registrationno' contains a disallowed function.
SQL Statement:>CREATE TABLE `invoicegeneration`.`registration` (
`invoice_no` SMALLINT(4) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT,
`amount` INT(11) NULL,
`name` VARCHAR(45) NOT NULL,
`invoice_date` DATETIME NULL,
`amt_in_words` VARCHAR(45) NULL,
`mop` VARCHAR(45) NULL,
`registrationcol` VARCHAR(45) NULL,
`dated` VARCHAR(45) NULL,
`drawn_on` VARCHAR(45) NULL,
`course` VARCHAR(45) NOT NULL,
`towards` VARCHAR(45) NULL,
`duration` VARCHAR(45) NULL,
`registrationno` VARCHAR(45) GENERATED ALWAYS AS
(CONCAT('SVSR',YEAR(CURDATE()),invoice_no)) STORED,
PRIMARY KEY (`invoice_no`),
UNIQUE INDEX `invoice_no_UNIQUE` (`invoice_no` ASC))
Can someone show me the right way to create the generated column

The problem is that you cannot create a virtual column based on non-deterministic function, as that would cause problems during replication.
See the “Limitations section in this blog post for more details.
You already have the invoice_date column, so a better solution would be to replace the column definition for registrationno with something like:
registrationno VARCHAR(45) GENERATED ALWAYS AS (CONCAT('SVSR',YEAR(invoice_date),invoice_no)) STORED
As a further optimization you could store the year of the invoice_date as a stand-alone column. This would allow you to place an index on it and then do analysis by year, or partition the table by year if your data grows huge.

Related

MySql Database migration

We have a large MySql table having around 400 million records, it is running on google cloud sql version 1 (MySql version 5.5).
Now google has come up with the new version of cloud sql (MySql version 5.6) ,so we want to migrate our database into this new sql version.This new version is available only on new instance. That means version 1 is on machine 1 and version 2 is on machine 2.
In this process we also made few schema changes, please check the following.
Old Schema
CREATE TABLE page_views_old (
domain varchar(50) DEFAULT NULL,
guid varchar(100) DEFAULT NULL,
sid varchar(100) DEFAULT NULL,
url varchar(2500) DEFAULT NULL,
ip varchar(20) DEFAULT NULL,
is_new varchar(20) DEFAULT NULL,
ref varchar(2500) DEFAULT NULL,
user_agent varchar(255) DEFAULT NULL,
stats_time datetime DEFAULT NULL,
country varchar(50) DEFAULT NULL,
region varchar(50) DEFAULT NULL,
city varchar(50) DEFAULT NULL,
city_lat_long varchar(50) DEFAULT NULL,
email varchar(100) DEFAULT NULL
)
New schema
CREATE TABLE page_views_new (
domain varchar(50) DEFAULT NULL,
guid binary(16) NOT NULL,
sid binary(16) NOT NULL,
url varchar(2500) DEFAULT NULL,
ip varbinary(16) NOT NULL,
is_new tinyint(4) NOT NULL,
ref varchar(2500) DEFAULT NULL,
user_agent varchar(255) DEFAULT NULL,
stats_time datetime DEFAULT NULL,
country char(2) NOT NULL,
region varchar(6) NOT NULL,
city varchar(50) DEFAULT NULL,
city_lat_long varchar(50) DEFAULT NULL,
email varchar(100) DEFAULT NULL,
id int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
)
As mentioned above we changed datatype varchar to binary, varchar to varbinary, varchar to tinyint, ip to binary and others. So my question is what is best way to load this database into new instance. I didn't mention here we need to add few indexes also.
My approach for this problem
1.Take a dump of complete Database.
2.Import into new instance.
3.Modify columns one by one using alter table statements.
4.Make it live.
Can you guys please specify is there any issue with this approach and suggest me best approaches. Please mention if any issues with schema also.
Thanks

Adding a table to MySQL keeps throwing up errors

Following a tutorial for web development. Here's the SQL code
CREATE TABLE 'users_relationships' (
'users_relationship_id' INT(10) NOT NULL,
'from_user_id' INT(10) NOT NULL,
'to_user_id' INT(10) unsigned NOT NULL,
'users_relationship_type' VARCHAR(10) NOT NULL,
'users_relationship_timestamp' DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY ('users_relationship_id'),
INDEX 'from_user_id' ('from_user_id'),
INDEX 'to_user_id' ('to_user_id'),
INDEX 'from_user_id_to_user_id' ('from_user_id', 'to_user_id'),
INDEX 'from_user_id_to_user_id_users_relationship_type' ('from_user_id', 'to_user_id', 'users_relationship_type'));
Whenever I run it i get the following error;
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''users_relationships' (
'users_relationship_id' INT(10) NOT NULL,
'from_' at line 1
I tried multiple changes but it keeps giving me the same error. The code is directly from the book. Any ideas?
You don't need backticks or single quotes. Just strain on the body. Single quotes however are downright syntax errors waiting to happen.
CREATE TABLE users_relationships (
users_relationship_id INT(10) NOT NULL,
from_user_id INT(10) NOT NULL,
to_user_id INT(10) unsigned NOT NULL,
users_relationship_type VARCHAR(10) NOT NULL,
users_relationship_timestamp DATETIME not null,
PRIMARY KEY (users_relationship_id),
INDEX from_user_id(from_user_id),
INDEX to_user_id (to_user_id),
INDEX from_user_id_to_user_id (from_user_id, to_user_id),
INDEX from_user_id_to_user_id_users_relationship_type (from_user_id, to_user_id, users_relationship_type)
);
Backticks are cute. But needed only when the the names would otherwise violate reserved words. Though they are generated by tools for create table dumps, why bother with them unless you really hate your little left pinky. Just my opinion.
If you want to use MySQL quotes, you need to use `, or else MySQL will mess up and think there is some string being created instead of table columns (typically for column names, you don't use ', or else MySQL will think a string is being used for data selection)
CREATE TABLE `users_relationships` (
`users_relationship_id` INT(10) NOT NULL,
`from_user_id` INT(10) NOT NULL,
`to_user_id` INT(10) unsigned NOT NULL,
`users_relationship_type` VARCHAR(10) NOT NULL,
`users_relationship_timestamp` DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`users_relationship_id`),
INDEX `from_user_id` (`from_user_id`),
INDEX `to_user_id` (`to_user_id`),
INDEX `from_user_id_to_user_id` (`from_user_id`, `to_user_id`),
INDEX `from_user_id_to_user_id_users_relationship_type` (`from_user_id`, `to_user_id`, `users_relationship_type`));
Here's the working SQLFiddle here
first u should replace all your single quotes with backticks.
CREATE TABLE `users_relationships` (
`users_relationship_id` INT(10) NOT NULL,
`from_user_id` INT(10) NOT NULL,
`to_user_id` INT(10) unsigned NOT NULL,
`users_relationship_type` VARCHAR(10) NOT NULL,
`users_relationship_timestamp` DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`users_relationship_id`),
INDEX `from_user_id` (`from_user_id`),
INDEX `to_user_id` (`to_user_id`),
INDEX `from_user_id_to_user_id` (`from_user_id`, `to_user_id`),
INDEX `from_user_id_to_user_id_users_relationship_type` (`from_user_id`, `to_user_id`, `users_relationship_type`));
second u cannot have DATETIME DEFAULT CURRENT_TIMESTAMP
or DATETIME DEFAULT ''
or TIMESTAMP DEFAULT CURRENT_TIMESTAMP

Need to generate an absent report based on days worked table data

This is my first question. So please bear with me if I have asked something stupid.
I have two tables in my mySql database named 'users', 'user_checkin_checkout'
Each time a user logs in to the website, I create a new record in 'user_checkin_checkout' table, and when user logs out, I update that record with current time.
Table structure:
# Users
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`address` varchar(255) NOT NULL,
`city` varchar(255) NOT NULL,
`state` varchar(255) NOT NULL,
`zip` varchar(255) NOT NULL,
`phone` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`last_login` datetime NOT NULL,
`date_added` datetime NOT NULL,
`date_modified` datetime NOT NULL,
PRIMARY KEY (`id`)
)
# CREATE TABLE IF NOT EXISTS `user_checkin_checkout` (
`user_id` int(11) NOT NULL,
`id` int(11) NOT NULL auto_increment,
`checkin_date` datetime NOT NULL,
`checkout_date` datetime NOT NULL,
`checkin_ip_address` varchar(255) NOT NULL,
`checkout_ip_address` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
)
Now, I need to generate a report so that it takes from and to date as parameters, and I want to know when the users were absent (not checked in). I am struggling to generate a query for this. I am unable to use join queries between them as that just brings when the user has actually checked in. Should I just have another table that populates all days between entered days and should I use a join with that table instead? Please advise.
Thanks in advance.

MySQL index help - which is faster?

What I'm dealing with:
I have a project which uses ActiveCollab 2, and the database structure is new to me - practically everything gets stored to a project_objects table and has a recursively hierarchical relationship:
Record 1234 might be type "Ticket" with parent_id of 123
Record 123 might be type "Category" with parent_id of 12
Record 12 might be type "Milestone" and so on.
Currently there are upwards of 450,000 records in this table and many of the queries in the code reference the name field which does NOT have an index on it. An example value might be Design or Development.
This might be an example query:
SELECT * FROM project_objects WHERE type = "Ticket" and name = "Design"
My problem:
I have a query that is taking upwards of 12-15 seconds and I have a feeling it's from that
name column lacking the index and requiring the full text search. My understanding with indexes is that if I add one to the name field, it'll speed up the reads, but slow down the inserts and updates. Does the index need to get rebuilt completely every time a record is added or updated or is it just altered/appended? I don't want to optimize this query with an index if it means drastically slowing down other parts of the code base which depend on faster writes.
My question:
Assume 100 reads and 100 writes per day, which is more likely to be a faster process for MySQL - executing the above query on the above table without the index or having to rebuild the index every time a record is added?
I don't have the knowledge or authority to start running benchmarks, but I would like to offer a suggestion to the client without sounding completely novice. Thanks!
EDIT: Here is the table:
'CREATE TABLE `project_objects` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`source` varchar(50) DEFAULT NULL,
`type` varchar(30) NOT NULL DEFAULT ''ProjectObject'',
`module` varchar(30) NOT NULL DEFAULT ''system'',
`project_id` int(10) unsigned NOT NULL DEFAULT ''0'',
`milestone_id` int(10) unsigned DEFAULT NULL,
`parent_id` int(10) unsigned DEFAULT NULL,
`parent_type` varchar(30) DEFAULT NULL,
`name` varchar(150) DEFAULT NULL,
`body` longtext,
`tags` text,
`state` tinyint(4) NOT NULL DEFAULT ''0'',
`visibility` tinyint(4) NOT NULL DEFAULT ''0'',
`priority` tinyint(4) DEFAULT NULL,
`created_on` datetime DEFAULT NULL,
`created_by_id` smallint(5) unsigned NOT NULL DEFAULT ''0'',
`created_by_name` varchar(100) DEFAULT NULL,
`created_by_email` varchar(100) DEFAULT NULL,
`updated_on` datetime DEFAULT NULL,
`updated_by_id` smallint(5) unsigned DEFAULT NULL,
`updated_by_name` varchar(100) DEFAULT NULL,
`updated_by_email` varchar(100) DEFAULT NULL,
`due_on` date DEFAULT NULL,
`completed_on` datetime DEFAULT NULL,
`completed_by_id` smallint(5) unsigned DEFAULT NULL,
`completed_by_name` varchar(100) DEFAULT NULL,
`completed_by_email` varchar(100) DEFAULT NULL,
`comments_count` smallint(5) unsigned DEFAULT NULL,
`has_time` tinyint(1) unsigned NOT NULL DEFAULT ''0'',
`is_locked` tinyint(3) unsigned DEFAULT NULL,
`estimate` float(9,2) DEFAULT NULL,
`start_on` date DEFAULT NULL,
`start_on_text` varchar(50) DEFAULT NULL,
`due_on_text` varchar(50) DEFAULT NULL,
`workflow_status` int(4) DEFAULT NULL,
`varchar_field_1` varchar(255) DEFAULT NULL,
`varchar_field_2` varchar(255) DEFAULT NULL,
`integer_field_1` int(11) DEFAULT NULL,
`integer_field_2` int(11) DEFAULT NULL,
`float_field_1` double(10,2) DEFAULT NULL,
`float_field_2` double(10,2) DEFAULT NULL,
`text_field_1` longtext,
`text_field_2` longtext,
`date_field_1` date DEFAULT NULL,
`date_field_2` date DEFAULT NULL,
`datetime_field_1` datetime DEFAULT NULL,
`datetime_field_2` datetime DEFAULT NULL,
`boolean_field_1` tinyint(1) unsigned DEFAULT NULL,
`boolean_field_2` tinyint(1) unsigned DEFAULT NULL,
`position` int(10) unsigned DEFAULT NULL,
`version` int(10) unsigned NOT NULL DEFAULT ''0'',
PRIMARY KEY (`id`),
KEY `type` (`type`),
KEY `module` (`module`),
KEY `project_id` (`project_id`),
KEY `parent_id` (`parent_id`),
KEY `created_on` (`created_on`),
KEY `due_on` (`due_on`)
KEY `milestone_id` (`milestone_id`)
) ENGINE=InnoDB AUTO_INCREMENT=993109 DEFAULT CHARSET=utf8'
As #Ray points out, indexes do not have to be rebuilt on every Insert, Update or Delete operation. So, if you only want to improve efficuency of this (or similar) queries, add either an index on (name, type) or on (type, name).
Since you already have an index on (type) alone, I would add the first one:
ALTER TABLE project_objects
ADD INDEX name_type_IDX
(name, type) ;
It may take a few seconds on a busy server but it has to be done once and then all the queries with conditions like yours will benefit. It may also improve efficiency of several other types of queries that involve name only or name and type:
WHERE name = 'Design' AND type = 'Ticket' --- your query
WHERE name = 'Design' --- condition on `name` only
GROUP BY name --- group by `name`
WHERE name LIKE 'Design%' --- range condition on `name` only
WHERE name = 'Design' --- equality condition on `name`
AND type LIKE 'Ticket%' --- and range condition on `type`
WHERE name = 'Design' --- equality condition on `name`
GROUP BY type --- and group by `type`
GROUP BY name --- group by `name`
, type --- and `type`
The insert cost of adding a single point index on the name column is most likely negligible--it will probably amount to an addition of a constant time increase, probably no more that a few milliseconds. You will eat up some extra disk space, but that's usually not a concern. Nothing like the multiple seconds you're experienceing on select performance.
Add the index, enjoy the performance improvement.
BTW: Indexes aren't 'rebuilt' on every insert. They're usually implemented in B-Trees and unless you're deleting frequently, should require very little re-balancing once you get larger than a few levels (and rebalancing with little depth is pretty cheap).

error in mysql partitioning

I need to create the table in the below format. But is returning the error in the partitioning area that is Error Code: 1659. Field 'fldassigndate' is of a not allowed type for this type of partitioning. How to resolve this error and make partitioning ?
CREATE TABLE tblattendancesetup (
fldattendanceid int(11) NOT NULL AUTO_INCREMENT,
flddept varchar(100) DEFAULT NULL,
fldemployee varchar(100) DEFAULT NULL,
fldintime varchar(20) DEFAULT NULL,
fldouttime varchar(20) DEFAULT NULL,
fldlateafter varchar(20) DEFAULT NULL,
fldearlybefore varchar(20) DEFAULT NULL,
fldweekoff varchar(20) DEFAULT NULL,
fldshiftname varchar(20) DEFAULT NULL,
fldassigndate varchar(20) DEFAULT NULL,
fldfromdate varchar(20) DEFAULT NULL,
fldtodate varchar(20) DEFAULT NULL,
fldrefid varchar(20) DEFAULT NULL,
UNIQUE KEY fldattendanceid (fldattendanceid),
KEY in_attendancesetup (fldemployee,fldintime,fldouttime,fldlateafter,fldearlybefore,fldfromdate,fldtodate,fldattendanceid),
KEY i_emp_tmp (fldemployee),
KEY i_emp_attendance (fldemployee)
)
PARTITION BY RANGE (fldassigndate)
(PARTITION p_Apr VALUES LESS THAN (TO_DAYS('2012-05-01')),
PARTITION p_May VALUES LESS THAN (TO_DAYS('2012-06-01')),
PARTITION p_Nov VALUES LESS THAN MAXVALUE );
From the MySQL manual (Section 18):
Data type of partitioning key. A partitioning key must be either an
integer column or an expression that resolves to an integer.
Neither dates nor varchars can be used for partitioning
Although this question is very old, I thought it'd help those searching, like I was at one point.
OP should first set his column structures correctly by using a correct structure for a date field, like datetime.
Once that is done, I have not tried to set my keys in that manner, but I have found that I needed to use a composite primary key if I wanted to keep the structure of my table the same. In this case, it'd be
PRIMARY KEY(`fldattendanceid`, `fldassigndate`)
Then, there is a missing TO_DAYS specified in the PARTITION BY RANGE. Lastly, I'm not 100 percent sure, but I don't think you can partition on a null field. Even if you can, it'd be awful practice to do so.
CREATE TABLE tblattendancesetup (
fldattendanceid INT(11) NOT NULL AUTO_INCREMENT,
flddept VARCHAR(100) DEFAULT NULL,
fldemployee VARCHAR(100) DEFAULT NULL,
fldintime DATETIME DEFAULT NULL,
fldouttime DATETIME DEFAULT NULL,
fldlateafter DATETIME DEFAULT NULL,
fldearlybefore DATETIME DEFAULT NULL,
fldweekoff VARCHAR(20) DEFAULT NULL,
fldshiftname VARCHAR(20) DEFAULT NULL,
fldassigndate DATETIME NOT NULL,
fldfromdate DATETIME DEFAULT NULL,
fldtodate DATETIME DEFAULT NULL,
fldrefid VARCHAR(20) DEFAULT NULL,
PRIMARY KEY(`fldattendanceid`, `fldassigndate`),
KEY in_attendancesetup (fldemployee,fldintime,fldouttime,fldlateafter,fldearlybefore,fldfromdate,fldtodate,fldattendanceid),
KEY i_emp_tmp (fldemployee),
KEY i_emp_attendance (fldemployee)
)
PARTITION BY RANGE ( TO_DAYS(fldassigndate))
(PARTITION p_Apr VALUES LESS THAN (TO_DAYS('2012-05-01')),
PARTITION p_May VALUES LESS THAN (TO_DAYS('2012-06-01')),
PARTITION p_Nov VALUES LESS THAN MAXVALUE );
I hope this helps someone!