Having a "Creation Date" column with default value at table creation - mysql

I want to have a column that will store the creation date of a row.
I'm using php and mysql but I don't think that matters.
I've looked for a series of answers about that but all of them seem to be for updating an existing table. Well surely there's one for what I'm looking for since it's a pretty basic question but I've yet to find it.
I've tried things with DEFAULT, CONSTRAINT but none of them allow me to create the table once added to my code. You could have the feeling that I'm not well versed in sql and you would not be wrong.
This creates the table, could you tell me what to add ?
CREATE TABLE IF NOT EXISTS artwork (
id_artwork int(4) NOT NULL AUTO_INCREMENT,
title varchar(50) NOT NULL,
creationDate DateTime(3),
CONSTRAINT PK_artwork PRIMARY KEY (id_artwork)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
I've tried the following with no success:
creationDate DateTime(3) DEFAULT GETDATE()
creationDate DateTime(3) DEFAULT (GETDATE())
creationDate DATETIME(3) DEFAULT (CURRENT_TIMESTAMP)
MySQL Version: 5.7.23 - MySQL Community Server (GPL)

From the MySQL documentation on initialization using DATETIME:
If a TIMESTAMP or DATETIME column definition includes an explicit fractional seconds precision value anywhere, the same value must be used throughout the column definition.
This means we'll have to carry forward your precision. I was able to get it to work on SQL Fiddle:
CREATE TABLE IF NOT EXISTS artwork (
id_artwork int(4) NOT NULL AUTO_INCREMENT,
title varchar(50) NOT NULL,
creationDate DateTime(3) DEFAULT CURRENT_TIMESTAMP(3),
CONSTRAINT PK_artwork PRIMARY KEY (id_artwork)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

Related

MySQL SELECT return wrong results

I'm working with MySQL 5.7. I created a table with a virtual column (not stored) of type DATETIME with an index on it. While I was working on it, I noticed that order by was not returning all the data (some data I was expecting at the top was missing). Also the results from MAX and MIN were wrong.
After I run
ANALYZE TABLE
CHECK TABLE
OPTIMIZE TABLE
then the results were correct. I guess there was an issue with the index data, so I have few questions:
When and why this could happen?
Is there a way to prevent this?
among the 3 command I run, which is the correct one to use?
I'm worried that this could happen in the future but I'll not notice.
EDIT:
as requested in the comments I added the table definition:
CREATE TABLE `items` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) unsigned DEFAULT NULL,
`image` json DEFAULT NULL,
`status` json DEFAULT NULL,
`status_expired` tinyint(1) GENERATED ALWAYS AS (ifnull(json_contains(`status`,'true','$.expired'),false)) VIRTUAL COMMENT 'used for index: it checks if status contains expired=true',
`lifetime` tinyint(4) NOT NULL,
`expiration` datetime GENERATED ALWAYS AS ((`create_date` + interval `lifetime` day)) VIRTUAL,
`last_update` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `expiration` (`status_expired`,`expiration`) USING BTREE,
CONSTRAINT `ts_competition_item_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `ts_user_core` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1312459 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED
Queries that were returning the wrong results:
SELECT * FROM items ORDER BY expiration DESC;
SELECT max(expiration),min(expiration) FROM items;
Thanks
TLDR;
The trouble is that your data comes from virtual columns materialized via indexes. The check, optimize, analyze operations you are doing forces the indexes to be synced and fixes any errors. That gives you the correct results henceforth. At least until the index gets out of sync again.
Why it may happen
Much of the problems are caused by issues with your table design. Let's start with.
`status_expired` tinyint(1) GENERATED ALWAYS AS (ifnull(json_contains(`status`,'true','$.expired'),false)) VIRTUAL
No doubt this is created to overcome the fact that you cannot directly index a JSON column in mysql. You have created a virtual column and indexed that instead. It's all very well, but this column can hold only one of two values; true or false. Which means it has very poor cadinality. As a result, mysql is unlikely to use this index for anything.
But we can see that you have combined the status_expired column with the expired column when creating the index. Perhaps with the idea of overcoming this poor cardinality mentioned above. But wait...
`expiration` datetime GENERATED ALWAYS AS ((`create_date` + interval `lifetime` day)) VIRTUAL,
Expiration is another virtual column. This has some repercussions.
When a secondary index is created on a generated virtual column,
generated column values are materialized in the records of the index.
If the index is a covering index (one that includes all the columns
retrieved by a query), generated column values are retrieved from
materialized values in the index structure instead of computed “on the
fly”.
Ref: https://dev.mysql.com/doc/refman/5.7/en/create-table-secondary-indexes.html#json-column-indirect-index
This is contrary to
VIRTUAL: Column values are not stored, but are evaluated when rows are
read, immediately after any BEFORE triggers. A virtual column takes no
storage.
Ref: https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html
We create virtual columns based on the sound principal that values generated by simple operations on columns shouldn't be stored to avoid redundancy, but by creating an index on it, we reintroduce redundancy.
Proposed fixes
based on the information provided, you don't really seem to need the status_expired column or even the expired column. An item that's past it's expiry date is expired!
CREATE TABLE `items` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) unsigned DEFAULT NULL,
`image` json DEFAULT NULL,
`status` json DEFAULT NULL,
`expire_date` datetime GENERATED ALWAYS AS ((`create_date` + interval `lifetime` day)) VIRTUAL,
`last_update` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `expiration` (`expired_date`) USING BTREE,
CONSTRAINT `ts_competition_item_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `ts_user_core` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1312459 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED
Simply compare the current date with the expired_date column in the above table when you need to find out which items have expired. The difference here is instead of expired being a calculated item in every query, you calculate the expiry_date once, when you create the record.
This makes your table a lot neater and queries possibly faster

insert not working on mysql 5.7.12-0?

my table structure is
CREATE TABLE IF NOT EXISTS `emp` (
`id` int(3) NOT NULL AUTO_INCREMENT,
`name` varchar(11) DEFAULT NULL,
`age` varchar(31) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
My query is :
INSERT INTO `emp` (`id`, `name`) VALUES ('1', 'prashant');
This is working with all the MYSQL versions below 5.7, but not working with MYSQL version 5.7.12-0ubuntu1
Getting error :
#1364 - Field 'age' doesn't have a default value
What is new in this version ??
Try it on mysql version below 5.7 ,you will see the difference.
Thanks :-)
It would be a huge surprise if this worked in any version of mysql at all. Copy paste this into sqlfiddle.com (mysql 5.6 or 5.5) and confirm for yourself.
age is defined as varchar(31) and not null. Thus your insert statement should have a value for that column. Or you should give it a default value. While you are at it, change it to a more appropriate data type.
CREATE TABLE IF NOT EXISTS `emp` (
`id` int(3) NOT NULL AUTO_INCREMENT,
`name` varchar(11) DEFAULT NULL,
`age` int(3) NOT NULL default 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
Updated:
Thinking about this some more I think you have switched off Strict Mode in your older version of mysql
Strict mode controls how MySQL handles invalid or missing values in
data-change statements such as INSERT or UPDATE. A value can be
invalid for several reasons. For example, it might have the wrong data
type for the column, or it might be out of range. A value is missing
when a new row to be inserted does not contain a value for a non-NULL
column that has no explicit DEFAULT clause in its definition. (For a
NULL column, NULL is inserted if the value is missing.) Strict mode
also affects DDL statements such as CREATE TABLE.
So my original statement is wrong! With string mode off, the default for varchar is probably '' (not sure though never used strict mode off)
In your table age described as not null.
`age` varchar(31) NOT NULL
So, it is required field for insert.
The NOT NULL constraint enforces a field to always contain a value. This means that you cannot insert a new record, or update a record without adding a value to this field.You have to give value for age also in your insert query because it cannot be null.For eg:-
insert into emp(`id`,`name`,`age`) values('1','rahul','26')
hope this helps!!.Comment for further query

SQL forcing me to use default value and property

Strange trouble indeed!
I am experiencing this issue when (My)SQL add some properties to my table, that I don´t want to be there and that I can´t change, even if I run right command and get "SUCCESS" reply.
Here is code for creating such a table:
CREATE TABLE `KIIS_EVENT_APPLICATION`
(
`ID_USER` smallint(3) unsigned NOT NULL,
`ID_EVENT` smallint(5) unsigned NOT NULL,
`COMES` timestamp,
`LEAVES` timestamp,
`TRANSPORT_THERE` varchar(30) COLLATE cp1250_czech_cs,
`TRANSPORT_BACK` varchar(30) COLLATE cp1250_czech_cs,
`ROLE` varchar(30) COLLATE cp1250_czech_cs NOT NULL,
`RELEVANCE` tinyint(1) unsigned NOT NULL,
FOREIGN KEY (`ID_EVENT`) REFERENCES `KIIS_EVENTS`(`ID_EVENT`),
FOREIGN KEY (`ID_USER`) REFERENCES `KIIS_USERS`(`ID_USER`)
) ENGINE=InnoDB DEFAULT CHARSET=cp1250 COLLATE cp1250_czech_cs
Let´s see the result:
Yellow highlighted things I don´t asked for.
If I run query, such as
ALTER TABLE `KIIS_EVENT_APPLICATION` CHANGE `COMES` `COMES` TIMESTAMP NOT NULL;
page says, it is successfully done, but nothing changes.
How can i make COMES column to be same as LEAVES column ?
Could it be caused by missing primary key? Do I need one when I have 2 foreign there (is it good SQL design practice, or?) ?
Michael - sqlbot got it right in comment.
ALTER TABLE KIIS_EVENT_APPLICATION MODIFY COLUMN COMES TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00'; or more correctly, ... TIMESTAMP NULL DEFAULT NULL
The first timestamp column in a table gets magical behavior by default prior to MySQL Server 5.6.
I added
timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
properties to all columns with such a behaviour and it works just fine.
Great!

Issue Converting varchar to nvarchar mysql

Sorry if this is an easy question, I am coming to MySQL from SQL Server.
When I execute my create statement it contains nvarchar but commits to the database as varchar. Even in my alter statement afterwards the column does not change at all. Does the collation or DB engine make a difference?
During execution I am not encountering any issues in results, other than the fact the column changes datatype. I attached a screencast of my activity http://screencast.com/t/wc94oei2
I have not been able to find anyone with similar issues through my Google searches
Did you mean, this..
CREATE TABLE stars (
idstars int(11) NOT NULL AUTO_INCREMENT,
Name nvarchar(200) DEFAULT NULL,
PRIMARY KEY (idstars),
UNIQUE KEY Name_UNIQUE (Name)
)
----turns to---
CREATE TABLE stars (
idstars int(11) NOT NULL AUTO_INCREMENT,
Name varchar(200) DEFAULT NULL,
PRIMARY KEY (idstars),
UNIQUE KEY Name_UNIQUE (Name)
)

Can we have primary key as function of column or set of column

I am trying to define a table where I want to define primary key has reverse of column. I was wondering if its possible in innodb ?
I am creating a table
create table abc
(
`id` varchar(255) PRIMARY KEY ,
`key` LONGTEXT NOT NULL,
`value` LONGTEXT NOT NULL ,
`last_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_on` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
)
but instead I want PRIMARY KEY (reverse(id))
MySQL doesn't support indexes on functions at all. So it clearly can't have them for a primary key.
Nor does MySQL support materialized views nor indices on views.1
Depending on what you're trying to accomplish, most likely you should just store your key the other way around. If your application depends on having it reverse of what's stored, create a view for the application to interact with. Unfortunately, updates will be difficult as MySQL doesn't do INSTEAD OF triggers or triggers on views at all.