Attaching simple metadata to a MySQL database - mysql

Is there a way to attach a piece of metadata to a MySQL database? I'm trying to write code to automatically update the database schema whenever a code upgrade requires it. This requires the storage of a single integer value -- the schema version. I could of course create a whole table for it, but that seems like overkill for just a simple number.

You can use table comments to store the version:
ALTER TABLE table1 COMMENT = '1.4';
You'll have to regex to get the comment from this:
SHOW CREATE TABLE table1;
/COMMENT='(.*)'/

To answer the question as titled, that is for metadata for the entire database and not individual tables, there are a couple of choices, depending on the privileges that you have.
The most direct route is to create a stored function, which requires the CREATE ROUTINE privilege. e.g.
mysql> CREATE FUNCTION `mydb`.DB_VERSION() RETURNS VARCHAR(15)
RETURN '1.2.7.2861';
Query OK, 0 rows affected (0.03 sec)
mysql> SELECT `mydb`.DB_VERSION();
+--------------+
| DB_VERSION() |
+--------------+
| 1.2.7.2861 |
+--------------+
1 row in set (0.06 sec)
If your privileges limit you to only creating tables, you can create a simple table and put the metadata as default values. There’s no need to store any data in the table.
mysql> CREATE TABLE `mydb`.`db_metadata` (
`version` varchar(15) not null default '1.2.7.2861');
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW COLUMNS FROM `mydb`.`db_metadata`;
+---------+-------------+------+-----+------------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+------------+-------+
| version | varchar(15) | NO | | 1.2.7.2861 | |
+---------+-------------+------+-----+------------+-------+
1 row in set (0.00 sec)

Related

Why does MySQL create temporay tables for renaming enum values?

I'm faced to rename a value of a enum value. According to the documentation enums are a
list of permitted values that are enumerated explicitly in the column specification at table creation time.
The ALTER TABLE Syntax documentation of MySQL states:
In most cases, ALTER TABLE makes a temporary copy of the original table.
However there are exceptions:
Changing the definition of an ENUM or SET column by adding new enumeration or set members to the end of the list of valid member values, as long as the storage size of the data type does not change.
or
Renaming a column.
For this exceptions no temporary table has to be created. Thus the ALTER query is faster.
From the statements above I concluded that MySQL won't have to create a temporary table for renaming just the value of an enum. After all: the corresponding (internal) number representation of the value stays the same. Therefore not even the assignment of a value within the table should be affected.
I ran some test queries on the expected behavior:
mysql> CREATE TABLE `test` (`type` enum('foo','bar') NOT NULL DEFAULT 'foo');
Query OK, 0 rows affected (0.01 sec)
mysql> show global status where Variable_name = 'Created_tmp_tables';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| Created_tmp_tables | 253 |
+--------------------+-------+
1 row in set (0.00 sec)
mysql> ALTER TABLE test CHANGE `type` type enum('foo','baz') NOT NULL DEFAULT 'foo';
Query OK, 0 rows affected (0.01 sec)
mysql> show global status where Variable_name = 'Created_tmp_tables';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| Created_tmp_tables | 254 |
+--------------------+-------+
1 row in set (0.00 sec)
The Created_tmp_tables shows that a temporary table was created during the altering process.
Is there a reason why MySQL behaves like this? Or is it just not optimized for such kind of operations?
I always forget how this works and every once in a while have the same question and am glad you asked this and posted the answer in your question :)
Changing the definition of an ENUM or SET column by adding new enumeration or set members to the end of the list of valid member values
is not the same thing as changing the ENUM value itself

Really strange error on mysql query

I have this query, and I think it talks by itself:
mysql> select id,email from members where email LIKE "%abraham.sustaita#gmail.com%";
+--------+----------------------------+
| id | email |
+--------+----------------------------+
| 272118 | abraham.sustaita#gmail.com |
+--------+----------------------------+
1 row in set (0.69 sec)
mysql> select id,email from members where email = "abraham.sustaita#gmail.com";
Empty set (0.00 sec)
mysql> select id,email from members where id = 272118;
Empty set (0.00 sec)
The data exists, but it returns empty if I use other than LIKE...
When there is such a flagrant impossible sequence of queries, then it's time to think about a table (or index) corruption and to run the Mysql CHECK command.
In that case, running REPAIR TABLE members QUICK did the trick.
If the id is a varchar and the email is a varchar they might have surrounding spaces.

MySQL gender table field

If I want to create a gender field in my table, how do I make sure that my database doesn't accept any value apart from "M" or "F" ?
$sqlCommand = "CREATE TABLE members (
id int(11) NOT NULL auto_increment,
...
...
...
...
gender
)";
Thank you
No triggers, no enums or other deamonic activities.
You can use a FOREIGN KEY to a reference table with just 2 rows:
CREATE TABLE Gender_Ref
( gender CHAR(1) NOT NULL,
PRIMARY KEY (gender)
) ENGINE = InnoDB ;
INSERT INTO Gender_Ref (gender)
VALUES
('F'), ('M') ;
CREATE TABLE members
( id int(11) NOT NULL auto_increment,
...
...
gender CHAR(1) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY gender
REFERENCES Gender_Ref (gender)
) ENGINE = InnoDB ;
It's also good advice to "lock" the reference table so the applications code has only read access. (That's usually good for most reference tables, and if you have an Admin application, you can of course give it write access as well to the reference tables).
Like pointed out in the comments, you can use ENUM like so:
gender ENUM('F','M') NOT NULL
However, you have to be careful as this will still accept the empty string too (although you'll get a warning for that):
mysql> create table t (g enum('M','F') not null);
Query OK, 0 rows affected (0.12 sec)
mysql> insert into t values ('M');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t values ('');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+----------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------+
| Warning | 1265 | Data truncated for column 'g' at row 1 |
+---------+------+----------------------------------------+
1 row in set (0.00 sec)
mysql> select * from t;
+---+
| g |
+---+
| M |
| |
+---+
2 rows in set (0.00 sec)
To ensure this does not happen, you could consider setting the sql_mode (http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html) to a more restrictive value:
mysql> set sql_mode = strict_all_tables;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t values ('');
ERROR 1265 (01000): Data truncated for column 'g' at row 1
However, you should investigate if this is a suitable option for you. Many existing applications (wordpress etc) don't like messing with the sql_mode so if your code is a plugin to those systems you want to avoid setting it.
You can choose to set the sql_mode server wide or session wide; The first option would be more robust, but requires configuring MySQL in a non default way, and is likely to affect other applications. Setting at the session level immediately after you open the connection should work just fine, but will clutter your application code. Pick your poison.
ypercube's suggestion to use a foreign key is also good, and is more portable to other RDBMSes than ENUM. However, you'll have to ensure your tables are both managed by the InnoDB engine. This is becoming more and more the standard so it's not a bad choice.
(if you're really paranoid, you should really ensure that the application only has read access to the gender reference table)
You could use enum type enum('M','F').
You can use a trigger to check if it is coreect or you can use an enum type enum('M','F')

Trigger with "NEW" doesn't work

it's the first time for me with triggers on MySQL.
I've two different tables ('users' and 'prova') and I want to insert a new row in 'prova' every time there is a new row in the 'users' table.
CREATE TRIGGER inserisciemail
AFTER INSERT ON users FOR EACH ROW
BEGIN
INSERT INTO prova (provaemail)
VALUES (NEW.email);
END
The field 'provaemail' results empty and only the id field is filled (autoincremented).
What's wrong?
Vito
I'm moving our discussion here because SO suggests to avoid extended discussions in comments.
So, thank you for the SQLs but I'm afraid they didn't include the CREATE TABLE for "prova" and the INSERT statement running on "users" table.
Anyways, I created the "users" table and the trigger on my dev environment. Then I created my own version of "prova" table as below:
CREATE TABLE `prova` (
`provaemail` VARCHAR(40)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Then ran the below insert statement:
INSERT INTO `users` (`id`, `email`) VALUES (1, 'guptaabhay#gmail.com');
And a new entry was inserted to "prova", here:
mysql> select * from prova;
+----------------------+
| provaemail |
+----------------------+
| guptaabhay#gmail.com |
+----------------------+
1 row in set (0.00 sec)
So the trigger worked!
It would be great if you could share the schema for "prova" and your INSERT INTO users query so that we can research further. I'm sure something's amiss.
EDIT 1
Thank you for the INSERTs. They ran fine and the "prova" table has now the following entries:
mysql> select * from prova;
+-----------------------+
| provaemail |
+-----------------------+
| genoveffa#dominio.it |
| peto#dominio.it |
| test#dominio.it |
| gianni#dominio.it |
| nuovissimo#dominio.it |
| new#dominio.it |
| vit#dominio.it |
+-----------------------+
8 rows in set (0.00 sec)
So nothing weird till now! Why don't you try these steps once at your end:
create "users" table
create trigger
create "prova" table (using the CREATE statement I've given above)
fire the inserts as given on http://pastie.org/3166828
do SELECT * FROM prova;
And let me know whatever you see?

2 servers, 2 memory tables, different sizes

I have got two servers both running a MySQL instance. The first one, server1, is running MySQL 5.0.22. The other one, server2, is running MySQL 5.1.58.
When I create a memory table on server1 and I add a row its size is instantly 8,190.0 KiB.
When I create a memory table on server2 and I add a row its size is still only some bytes, though.
Is this caused by the difference in MySQL version or (hopefully) is this due to some setting I can change?
EDIT:
I haven't found the reason for this behaviour yet, but I did found a workaround. So, for future references, this is what fixed it for me:
All my memory tables are made once and are read-only from thereon. When you specify to MySQL the maximum number of rows your table will have, its size will shrink. The following query will do that for you.
ALTER TABLE table_name MAX_ROWS = N
Factor of 2?
OK, the problem likely is caused by the UTF-8 vs latin1
:- http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html
You can check the database connection, database default character set for both servers.
here is the testing I have just done :-
mysql> create table test ( name varchar(10) ) engine
-> =memory;
Query OK, 0 rows affected (0.03 sec)
mysql> show create table test;
+-------+------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+------------------------------------------------------------------------------------------------+
| test | CREATE TABLE `test` (
`name` varchar(10) DEFAULT NULL
) ENGINE=MEMORY DEFAULT CHARSET=latin1 |
+-------+------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> insert into test values ( 1 );
mysql> set names utf8;
Query OK, 0 rows affected (0.01 sec)
mysql> create table test2 ( name varchar(10) ) engine =memory default charset = utf8;
Query OK, 0 rows affected (0.01 sec)
Query OK, 0 rows affected (0.01 sec)
mysql> insert into test2 values ( convert(1 using utf8) );
Query OK, 1 row affected (0.01 sec)
mysql> select table_name, avg_row_length from information_schema.tables where TABLE_NAME in( 'test2', 'test');
+------------+----------------+
| table_name | avg_row_length |
+------------+----------------+
| test | 12 |
| test2 | 32 |
+------------+----------------+
2 rows in set (0.01 sec)