I discovered something strange today when I was trying to select sum from an ENUM field. It was adding 1 to the values! A sample table I used is shown below:
x y
_______
3 | 2
0 | 1
Both x and y are ENUM ('0','1','2','3')
My query is as follows:
select sum(x), sum(y), sum(x+y) from myfield
And the result is:
5 5 10
This is very strange. Does it mean that is this behaviour consistent? Can I use something like:
select sum(x - 1), sum(y - 1), sum((x-1)+(y-1)) from myfield
which will produce the correct results or is this behavior only peculiar to my db server in particular?
Thanks.
EDIT:
For those wondering why I'm using ENUM, it's because the actual field I'm using contains 'AR' which can't fit in into tinyint.
As an ENUM field is really just an INT UNSIGNED, it will not work as you expect, if you use integer values for the ENUMs. For example, your ENUM of '0', is stored internally as a numeric 1, and your '1', is stored as a numeric 2. Surprisingly, the empty string '' is stored internally as a 0. Here's an example of this not working as expected:
mysql> CREATE TABLE enumtest (
-> id int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> a ENUM ('0','1','2','3') NOT NULL DEFAULT '0',
-> i int unsigned NOT NULL DEFAULT 0
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO enumtest SET a = 0, i=0;
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> SHOW WARNINGS;
+---------+------+----------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------+
| Warning | 1265 | Data truncated for column 'a' at row 1 |
+---------+------+----------------------------------------+
1 row in set (0.00 sec)
mysql> INSERT INTO enumtest SET a = '0', i=0;
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO enumtest SET a = 1, i=1;
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO enumtest SET a = '1', i=1;
Query OK, 1 row affected (0.00 sec)
mysql> SELECT a,0+a,i FROM enumtest;
+---+-----+---+
| a | 0+a | i |
+---+-----+---+
| | 0 | 0 |
| 0 | 1 | 0 |
| 0 | 1 | 1 |
| 1 | 2 | 1 |
+---+-----+---+
4 rows in set (0.00 sec)
mysql> SELECT SUM(a),SUM(0+a), SUM(i) FROM enumtest;
+--------+----------+--------+
| SUM(a) | SUM(0+a) | SUM(i) |
+--------+----------+--------+
| 4 | 4 | 2 |
+--------+----------+--------+
1 row in set (0.00 sec)
The clause 0+a coerces the ENUM to its underlying UNSIGNED value. This is equivalent to CAST(a AS UNSIGNED).
This is because doing SUM() on an enum column doesn't do what you think it does. The data stored for an enum column type is the index into the enum, that index starts at 1, and it's this index that mysql will SUM()
Your table looks like this when displayed:
x y
_______
3 | 2
0 | 1
This is showing your enum values - you just happened to define your enum values to be numbers too. You could have defined them to be e.g. ENUM ('blue','red','green','yellow')
And it would have looked like:
x y
_______________
yellow | green
blue | red
However, this is for display only. What is actually stored in the rows for this table is
the index in the enum.
The elements listed in the column specification are assigned index
numbers, beginning with 1.
So those indexes starts at 1. it's these underlying data that SUM() and other aggregate functions works at for an ENUM column. There's no implicit conversion to the enum values, which you defined as numbers too.
i.e. the data stored is these indexes:
x y
_______
4 | 3
1 | 2
And while it doesn't really make sense to SUM enums, it is those indexes that mysql would aggregate when using SUM()
The docs is a must read.
Related
How can I make MySQL auto increment in 4 digit format?
So instead of '1' make '0001'?
Try adding ZEROFILL attribute to the field.
Could you leave it as an integer and format it for humans in your SQL, for example, to pad with zeros to 4 chars wide
select lpad(idcolumn,4,'0') from mytable;
Or use zerofill and specify the desired width when declaring the table:
create table tmpfoo (
mykey int(6) zerofill not null auto_increment,
primary key(mykey)
);
insert into tmpfoo values(1),(2);
select * from tmpfoo;
+--------+
| mykey |
+--------+
| 000001 |
| 000002 |
+--------+
MySQL supports ZEROFILL on integer columns:
mysql> create table foo (the_key int unsigned zerofill not null
auto_increment primary key);
Query OK, 0 rows affected (0.21 sec)
mysql> insert into foo SET the_key = Null;
Query OK, 1 row affected (0.00 sec)
...
mysql> insert into foo SET the_key = Null;
Query OK, 1 row affected (0.00 sec)
mysql> select * from foo;
+------------+
| the_key |
+------------+
| 0000000001 |
| 0000000002 |
| 0000000003 |
| 0000000004 |
| 0000000005 |
| 0000000006 |
| 0000000007 |
| 0000000008 |
+------------+
8 rows in set (0.00 sec)
You may need to look into using a smallint (5 digits), or trimming/padding.
If you need the auto_increment column in a zero padded format, I suggest that you display it as such and not attempt to store it in the database that way.
In PHP, you could use the following code to display or otherwise use the id:
$padded_id = str_pad($id, 4, '0');
To pad in the database set the id column to ZEROFILL
But if its for display purposes only I recommend using LPAD
SELECT RIGHT('000000' + yourNum, 6);
is the field an integer? if so, the answer is, "why? it's an integer!" ;-)
I'm trying to write an update that will increment the value of auth_id; however, if there is no value, I'd like to set it to 1. I can't even get the select to give me a 0 value to increment.
mysql> describe sequences;
+---------+--------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------------------+------+-----+---------+-------+
| auth_id | int(5) unsigned zerofill | NO | | 00000 | |
+---------+--------------------------+------+-----+---------+-------+
I thought this would work.
mysql> select ifnull(auth_id,0) from sequences;
Empty set (0.00 sec)
Or this...
mysql> select coalesce(auth_id, 0) from sequences;
Empty set (0.00 sec)
What am I missing?
This seems to work:
select ifnull(sequences.auth_id, x.auth_id) as auth_id from sequences right join (select 1 as auth_id) x on 1;
The assumption is that there's only 1 row in sequences. The trick is to make a right join to a fake table with only one row of its own, and then use the ifnull to get the wanted value.
I have a BEFORE INSERT TRIGGER which is used to calculate the AUTO_INCREMENT value of a column (id_2).
id_1 | id_2 | data
1 | 1 | 'a'
1 | 2 | 'b'
1 | 3 | 'c'
2 | 1 | 'a'
2 | 2 | 'b'
2 | 3 | 'c'
2 | 4 | 'a'
3 | 1 | 'b'
3 | 2 | 'c'
I have PRIMARY(id_1, id_2) and I am using InnoDB. Before, the table was using MyISAM and I've had no problems: id_2 was set to AUTO_INCREMENT, so each new entry for id_1 would generate new id_2 on its own. Now, after switching to InnoDB, I have this trigger to do the same thing:
SET #id = NULL;
SELECT COALESCE(MAX(id_2) + 1, 1) INTO #id FROM tbl WHERE id_1 = NEW.id_1;
SET NEW.id_2= #id;
It works perfectly, except now the LAST_INSERT_ID() has wrong value (it returns 0). A lot of code depends on the LAST_INSERT_ID() being correct. However since MySQL 5.0.12 any changes made to LAST_INSERT_ID within TRIGGERS are not affecting the global value. Is there any way to bypass this? I can easily set the AFTER UPDATE TRIGGER which changes the LAST_INSERT_ID by calling LAST_INSERT_ID(NEW.id_2), however any client-side would get LAST_INSERT_ID set to 0.
Is there any working work-around to force MySQL to maintain the state of LAST_INSERT_ID which was changed inside the trigger? Is there any alternative, other than switching back to MyISAM which supports this out of the box or running another SELECT max(id_2) FROM tbl WHERE id_1 = :id as part of the transaction to ensure that the row found will be the one inserted earlier?
> SHOW CREATE TABLE tbl;
CREATE TABLE `tbl` (
`id_1` int(11) NOT NULL,
`id_2` int(11) NOT NULL,
`data` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id_1`,`id_2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
Example:
INSERT INTO tbl (id_1, id_2, data) VALUES (1, NULL, 'd');
SELECT LAST_INSERT_ID();
The first statement will insert the row 1 | 4 | 'd' into the table. The second statement will return 0, but I need it to return 4.
As asked by Ravinder Reddy, adding the short explanation about the system:
I have a table that contains baskets, and I have another table (tbl) that contains items. The basket is created by the application and is assigned an ID from AUTO_INCREMENT on baskets' table. The task is to insert items in basket with id = id_1, into tbl, assigning them a unique ID within that basket's scope. Each item has some data associated with it, which may repeat within the same basket. So in practice, I am trying to store all the data entries within a single basket, and then be able to refer to (and retrieve) these individual entries by their id_1-id_2 pairs.
With your table structure description, it is clear that it does not have a primary key field whose values can be auto generated. MySQL's information_schema.tables does not hold auto_increment value but null for those fields which are not defined auto_increment.
Trigger issue:
The code block used in your trigger body seems depending on explicit calculation and input for the id fields. It did not use the default behaviour of an auto_increment field.
As per MySQL's documentation on LAST_INSERT_ID:
LAST_INSERT_ID() returns a BIGINT UNSIGNED (64-bit) value
representing the first automatically generated value
successfully inserted for an AUTO_INCREMENT column
as a result of the most recently executed INSERT statement.
It is clear that it is for auto_increment fields only.
None of the fields id_1 and id_2 are attributed auto_increment.
Due to the reason, though you pass null as input for those fields while inserting, no value will be auto generated and assigned to them.
Alter your table to set auto_increment to one of those id_x fields, and then start inserting values. One caution is that passing a value explicitly to an auto_increment field during insertion will cause last_insert_id return a zero or most recent auto generated value, but not the NEW.id. Passing a null or not choosing the auto_increment field during insertion will trigger generation of NEW value for that field and last_insert_id can pick and return it.
Following example demonstrates above behaviour:
mysql> drop table if exists so_q27476005;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> create table so_q27476005( i int primary key );
Query OK, 0 rows affected (0.33 sec)
Following statement shows next applicable auto_increment value for a field.
mysql> select auto_increment
-> from information_schema.tables
-> where table_name='so_q27476005';
+----------------+
| auto_increment |
+----------------+
| NULL |
+----------------+
1 row in set (0.00 sec)
Let us try inserting a null value into the field.
mysql> insert into so_q27476005 values( null );
ERROR 1048 (23000): Column 'i' cannot be null
Above statement failed because input was into a not null primary key field but not attributed for auto_increment. Only for auto_increment fields, you can pass null inputs.
Now let us see the behaviour of last_insert_id:
mysql> insert into so_q27476005 values( 1 );
Query OK, 1 row affected (0.04 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 0 |
+------------------+
1 row in set (0.00 sec)
As the input was explicit and also the field is not attributed for auto_increment,
call for last_insert_id resulted a 0. Note that, this can also be some value else,
if there was another insert call for any other auto_increment field of another table,
in the same database connection session.
Let us see the records in the table.
mysql> select * from so_q27476005;
+---+
| i |
+---+
| 1 |
+---+
1 row in set (0.00 sec)
Now, let us apply auto_increment to the field i.
mysql> alter table so_q27476005 change column i i int auto_increment;
Query OK, 1 row affected (0.66 sec)
Records: 1 Duplicates: 0 Warnings: 0
Following statement shows next applicable auto_increment value for the field i.
mysql> select auto_increment
-> from information_schema.tables
-> where table_name='so_q27476005';
+----------------+
| auto_increment |
+----------------+
| 2 |
+----------------+
1 row in set (0.00 sec)
You can cross check that the last_insert_id still is the same.
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 0 |
+------------------+
1 row in set (0.00 sec)
Let us insert a null value into the field i.
mysql> insert into so_q27476005 values( null );
Query OK, 1 row affected (0.03 sec)
It succeeded though passing a null to a primary key field,
because the field is attributed for auto_increment.
Let us see which value was generated and inserted.
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)
And the next applicable auto_increment value for the field i is:
mysql> select auto_increment
-> from information_schema.tables
-> where table_name='so_q27476005';
+----------------+
| auto_increment |
+----------------+
| 3 |
+----------------+
1 row in set (0.00 sec)
mysql> select * from so_q27476005;
+---+
| i |
+---+
| 1 |
| 2 |
+---+
2 rows in set (0.00 sec)
Now, let us observe how last_insert_id results when explicit input is given for the field.
mysql> insert into so_q27476005 values( 3 );
Query OK, 1 row affected (0.07 sec)
mysql> select * from so_q27476005;
+---+
| i |
+---+
| 1 |
| 2 |
| 3 |
+---+
3 rows in set (0.00 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)
You can see that last_insert_id did not capture the value due to explicit input.
But, information schema do registered next applicable value.
mysql> select auto_increment
-> from information_schema.tables
-> where table_name='so_q27476005';
+----------------+
| auto_increment |
+----------------+
| 4 |
+----------------+
1 row in set (0.08 sec)
Now, let us observe how last_insert_id results when input for the field is auto/implicit.
mysql> insert into so_q27476005 values( null );
Query OK, 1 row affected (0.10 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 4 |
+------------------+
1 row in set (0.00 sec)
Hope, these details help you.
I knew boolean in mysql as tinyint (1).
Today I see a table with defined an integer like tinyint(2), and also others like int(4), int(6) ...
What does the size means in field of type integer and tinyint ?
The (m) indicates the column display width; applications such as the MySQL client make use of this when showing the query results.
For example:
| v | a | b | c |
+-----+-----+-----+-----+
| 1 | 1 | 1 | 1 |
| 10 | 10 | 10 | 10 |
| 100 | 100 | 100 | 100 |
Here a, b and c are using TINYINT(1), TINYINT(2) and TINYINT(3) respectively. As you can see, it pads the values on the left side using the display width.
It's important to note that it does not affect the accepted range of values for that particular type, i.e. TINYINT(1) still accepts [-128 .. 127].
It means display width
Whether you use tinyint(1) or tinyint(2), it does not make any difference.
I always use tinyint(1) and int(11), I used several mysql clients (navicat, sequel pro).
It does not mean anything AT ALL! I ran a test, all above clients or even the command-line client seems to ignore this.
But, display width is most important if you are using ZEROFILL option, for example your table has following 2 columns:
A tinyint(2) zerofill
B tinyint(4) zerofill
both columns has the value of 1, output for column A would be 01 and 0001 for B, as seen in screenshot below :)
mysql> CREATE TABLE tin3(id int PRIMARY KEY,val TINYINT(10) ZEROFILL);
Query OK, 0 rows affected (0.04 sec)
mysql> INSERT INTO tin3 VALUES(1,12),(2,7),(4,101);
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM tin3;
+----+------------+
| id | val |
+----+------------+
| 1 | 0000000012 |
| 2 | 0000000007 |
| 4 | 0000000101 |
+----+------------+
3 rows in set (0.00 sec)
mysql>
mysql> SELECT LENGTH(val) FROM tin3 WHERE id=2;
+-------------+
| LENGTH(val) |
+-------------+
| 10 |
+-------------+
1 row in set (0.01 sec)
mysql> SELECT val+1 FROM tin3 WHERE id=2;
+-------+
| val+1 |
+-------+
| 8 |
+-------+
1 row in set (0.00 sec)
About the INT, TINYINT... These are different data types, INT is 4-byte number, TINYINT is 1-byte number. More information here - INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT.
The syntax of TINYINT data type is TINYINT(M), where M indicates the maximum display width (used only if your MySQL client supports it).
Numeric Type Attributes.
I have the following table
id name address empid
1 AA aa 0
2 BB bb 0
3 CC cc 0
I need to write a query to set empid starting from 1. How to write it please. Do i have to use a stored procedure to that or can do it with a normal query?
Thank You.
Here is a way to do it that utilizes a pretty obscure assignment operator in MySQL. This solution won't skip numbers in the case of gaps in the primary key sequence like some of the other solutions.
set #count = 0;
update test set empid = #count := #count+1;
Here is the proof:
mysql> create table test (
-> id int unsigned primary key auto_increment,
-> name varchar(32) not null,
-> address varchar(32) not null,
-> empid int unsigned not null default 0
-> ) engine=innodb;
Query OK, 0 rows affected (0.02 sec)
mysql> insert into test (name, address)
-> values ('AA', 'aa'), ('BB', 'bb'), ('CC', 'cc');
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from test;
+----+------+---------+-------+
| id | name | address | empid |
+----+------+---------+-------+
| 1 | AA | aa | 0 |
| 2 | BB | bb | 0 |
| 3 | CC | cc | 0 |
+----+------+---------+-------+
3 rows in set (0.00 sec)
mysql> set #count=0;
Query OK, 0 rows affected (0.00 sec)
mysql> update test set empid = #count := #count+1;
Query OK, 3 rows affected (0.00 sec)
Rows matched: 3 Changed: 3 Warnings: 0
mysql> select * from test;
+----+------+---------+-------+
| id | name | address | empid |
+----+------+---------+-------+
| 1 | AA | aa | 1 |
| 2 | BB | bb | 2 |
| 3 | CC | cc | 3 |
+----+------+---------+-------+
3 rows in set (0.00 sec)
If you are looking to put 1 in empid for the first row, 2 for the second, etc. the easiest way would be to use your id field that is already doing this like so:
UPDATE table
SET empid = id
The only thing you need to worry about is missing numbers in the id column. If that would be an issue and you are missing id numbers, you will have to use a different method. To do that, you would need to do something like this:
DECLARE #counter int
SET #counter = 1
UPDATE table
SET #counter = empid = #counter + 1
As #BiggsTRC suggested you can use id to set empid. If not, you can create stored procedure or some PHP code to do that.
If your ID is not "AutoIncrement" field, you can consider a new column as autoincrement field and assign that value to emp with update query and later delete that new column. (These are some alternates, you need to choose the best one)
UPDATE `test` SET `empid`=`id`
But why would you want to do it? It's pretty much definition of redundancy.