What is the utility of tinyint(M) in MySQL? - mysql

This is more of an academic question. In a MySQL database, I know tinyint(M) and its differences from tinyint(1) have already been discussed on this site, but I'm wondering something else.
Could a column "REVIEWED TINYINT(5) be used to store the values of 5 different boolean checkboxes in the frontend form? I'm thinking along the lines of treating it as an array. If so, how would they be referenced? Would something like REVIEWED(3) or REVIEWED[3] work for referencing their elements? Would there be a syntax for checking whether all their elements were 1 or 0 (or null)?

TINYINT(1) and TINYINT(5) and TINYINT(12) or any other length are actually stored exactly the same. They are all an 8-bit signed integer. They support integer values from -128 to 127. Or values from 0 to 255 if the column is defined as an unsigned integer.
What's with the "length" argument then? Nothing. It doesn't affect the size of the integer or the number of bits or the range of values. The argument is a display hint only. It's useless unless you use the ZEROFILL option.
mysql> create table mytable (i1 tinyint(1) zerofill, i2 tinyint(5) zerofill, i3 tinyint(12) zerofill);
Query OK, 0 rows affected (0.04 sec)
mysql> insert into mytable values (255,255,255);
Query OK, 1 row affected (0.02 sec)
mysql> select * from mytable;
+------+-------+--------------+
| i1 | i2 | i3 |
+------+-------+--------------+
| 255 | 00255 | 000000000255 |
+------+-------+--------------+
The ZEROFILL option forces the column to be unsigned, and when you query the column, it pads the result with zeroes up to the length you defined for the column. The zeroes are not stored in the database, they are added only when you fetch query results.
The "length" argument of integers is misleading, and it causes a lot of confusion for MySQL users. In hindsight, it would have been better to make the syntax like TINYINT ZEROFILL(12) but it's too late to change it now.

Related

Is there really performance different between of string and int cloumn(table design)?

I am interested in this issue. Every time I design a table, I have this doubt. Take table posts as an example, it contains a column named post_type which could be one of the following value:
post(varchar) or 1(tinyint)
page(varchar) or 2(tinyint)
revision(varchar) or 3(tinyint)
The problem is that what type should I use for that column. varchar makes query results will be more intuitive, I dont need to figure out what 1/2/3 mean.
As to tinyint, does it perform better than varchar?
PS: I am using MySQL.
Data types don't have performance. They are a storage format.
Queries do have performance. So to evaluate performance, you should be specific about which query you are trying to measure.
In a query that merely fetches the row by its primary key, there's no practical difference. InnoDB keeps columns for a given row together on a page, so once it has fetched the page from disk into RAM, all the columns are available. The difference between reading 4 bytes for an integer vs. reading 8 bytes for a string like 'revision' is insignificant.
SELECT post_type FROM posts WHERE post_id = 8675309;
If you're looking up rows by their post_type value, then it becomes a little more important, because it needs to do some comparison to evaluate each row to see if it should be included in the result. Depending on the number of rows, and whether you have an index, the difference between string comparisons and integer comparisons could be important.
SELECT ... FROM posts WHERE post_type = 'revision';
I created a table and filled it with > 1 million rows:
create table posts (
post_id serial primary key,
post_type_utf varchar(10),
post_type_bin varbinary(10),
post_type_int int
);
Then I timed how long it takes to search the whole table:
select count(*) from posts where post_type_utf = 'revision';
+----------+
| count(*) |
+----------+
| 1048576 |
+----------+
1 row in set (0.24 sec)
mysql> select count(*) from posts where post_type_bin = binary 'revision';
+----------+
| count(*) |
+----------+
| 1048576 |
+----------+
1 row in set (0.15 sec)
mysql> select count(*) from posts where post_type_int = 1;
+----------+
| count(*) |
+----------+
| 1048576 |
+----------+
1 row in set (0.15 sec)
The time suggests that searching for an integer is about the same as searching for a binary string.
Why is a utf8 string slower? Because every string comparison has to evaluate character by character, against the collation defined for the column. A binary string comparison can just use memcmp() to compare the whole string in one operation.
It's also important to consider that indexes are usually a greater factor for performance than which data type you choose. Indexes help because your query for a specific post_type value will only examine those rows that match.
But in this case, you only have a few distinct values for the post_type, so a search in an index is likely to match many rows regardless.
If you're going to use them as numbers, TINYINT(1) is definitely better as mysql won't need to do unnecessary conversions. For 1-character strings you could use CHAR(1) or ENUM.

Why MYSQL SUM() on FLOAT columns generates fractions

Could someone tell me the reason why MySQL SUM() function performed on FLOAT columns gives strange result ?
Example :
CREATE TABLE payments (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
amount FLOAT DEFAULT NULL,
PRIMARY KEY(id)
);
INSERT INTO payments (amount) VALUES (1.3),(1.43),(1.65),(1.71);
When performing SUM(), expecting 6.09, MySQL returns that floating number :
mysql> SELECT SUM(amount) FROM payments WHERE 1;
+--------------------+
| SUM(amount) |
+--------------------+
| 6.0899999141693115 |
+--------------------+
1 row in set (0.00 sec)
This is pretty scary for a guy that may develop let say... an accounting software ! :/
Version : Mysql 5.5.60
That's why you don't use floating point values in anything where rounding is important. Even the manual says:
The DECIMAL and NUMERIC types store exact numeric data values. These types are used when it is important to preserve exact precision, for example with monetary data.

MySQL insert into table with 00001 automatically converts to 1

I have a table with a column id INT(11). When I do:
insert into table (id) VALUES ('00001');
it converts it to 1. I don't mind this behavior but I am wondering if this will change in future versions of mysql. Is there documentation or a reason on why this works?
This is because of the ZEROFILL flag on the column. If the flag is set, it adds zeros as padding to your numbers so that they meet the total "width" of the number. So if you have an INT(3) and you INSERT 1, you get 001.
In your case, you probably have an INT(1) so it is just truncating your leading zeros because they do not affect the value of the number—only the display of the number.
What is the benefit of zerofill in MySQL?

What's the purpose of varchar(0)

I recently encountered a problem caused by a typo in the database creation script, whereby a column in the database was created as varchar(0) instead of varchar(20).
I expected that I would have gotten an error for 0-length string field, but I didn't. What is the purpose of varchar(0) or char(0) as I wouldn't be able to store any data in this column anyway.
It's not allowed per the SQL-92 standard, but permitted in MySQL. From the MySQL manual:
MySQL permits you to create a column of type CHAR(0). This is useful primarily when you have to be compliant with old applications that depend on the existence of a column but that do not actually use its value. CHAR(0) is also quite nice when you need a column that can take only two values: A column that is defined as CHAR(0) NULL occupies only one bit and can take only the values NULL and '' (the empty string).
Just checked MySQL, it's true that it allows zero-length CHAR and VARCHAR.
Not that it can be extremely useful but I can think of a situation when you truncate a column to 0 length when you no longer need it but you don't want to break existing code that writes something there. Anything you assign to a 0-length column will be truncated and a warning issued, but warnings are not errors, they don't break anything.
As they're similar types, char and varchar, I'm going to venture to guess that the use-case of varchar(0) is the same as char(0).
From the documentation of String Types:
MySQL permits you to create a column of type CHAR(0). This is useful
primarily when you have to be compliant with old applications that
depend on the existence of a column but that do not actually use its
value. CHAR(0) is also quite nice when you need a column that can take
only two values: A column that is defined as CHAR(0) NULL occupies
only one bit and can take only the values NULL and '' (the empty
string).
It's useful in combination with a unique index for if you want to mark one specific row in your table (for instance, because it serves as a default). The unique index ensures that all other rows have to be null, so you can always retrieve the row by that column.
You can use it to store boolean values.
Look this code:
mysql> create table chartest(a char(0));
Query OK, 0 rows affected (0.26 sec)
mysql> insert into chartest value(NULL);
Query OK, 1 row affected (0.01 sec)
mysql> insert into chartest value('');
Query OK, 1 row affected (0.00 sec)
mysql> select 'true' from chartest where a is null;
+------+
| true |
+------+
| true |
+------+
1 row in set (0.00 sec)
mysql> select 'false' from chartest where a is not null;
+-------+
| false |
+-------+
| false |
+-------+
1 row in set (0.00 sec)
We can use NULL to represent true and '' (empty string) to represent false!
According to MySQL reference manual, only NULL occupies one bit.

MySQL Fulltext search: How do I match a string with a minus char?

I have a table
CREATE TABLE `ftx_utf8` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`col1` varchar(45) NOT NULL,
`col2` text NOT NULL,
PRIMARY KEY (`id`),
FULLTEXT KEY `FTX` (`col1`,`col2`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
with the content
|id|col1 |col2 |
|1 |255-381| |
|2 | |255-381|
|3 |255381 | |
|4 | |255381 |
Now If I execute the query:
SELECT id, MATCH(col1, col2) AGAINST ('"255381"' IN BOOLEAN MODE) AS match1
FROM ftx_utf8
HAVING match1 > 0
it returns rows 3 and 4
But this query
SELECT id, MATCH(col1, col2) AGAINST ('"255-381"' IN BOOLEAN MODE) AS match1
FROM ftx_utf8
HAVING match1 > 0
does not return rows 1 and 2 as expected.
How can I match 255-381 with MySQL Fulltext search?
The odd thing is that a customer claims it worked before.
But last week we did an update of his database from an early 5.0 installation to a 5.1.54 server (Windows).
I fixes the problem. Or at least I know what is happening:
MySQL ignores certain keywords in his fulltext index. If you have a column containing 255-381, mysql stores 255 and 381 in his full text index as single words. Or to be more precise, by default, these two values are even ignored because the minimum word length, specified by ft_min_word_len=? is 4. But if you set the value to 3 and serch for 255-381 the fulltext search engine searches for the exact phrase "255 381" and returns the row containing 255-381 because 255 followed by 381 is stored in the index.
That works for the example provided in the initial question but is not a perfect solution, because it does not apply for values with 2 chars or even one char followed by a minus.
Let's assume I want to search for 25-5381. 25 is skipped because it is shorter than the ft_min_word_len threshold. So a search for 25-5381 is basically the same as a search for 5381 which would also return the rows containing 26-5381 5381 and 5381-12
Since I don't have the time to switch to a better full text search engine like sphynx or lucene and a LIKE query would be to slow, I suppose I will use a workaround:
I will add another column containing all entries from col1 and col2 but replace "-" chars with special string like MINUS and will perform a fulltext query on that column with 255MINUS381 which should do the trick.
Sorry but, did u try with REGEX (Regular Expressions) instead? And... why do u use double quotes? Just ' or ", not both. Even more, why you use text and varchar? I think you come from other SQL language, try just MySQL
Just find nice way to do this.
SELECT (
MATCH(`name`) AGAINST('+$keyword*' IN BOOLEAN MODE) + (`name`=$keyword)*2
) as `rel`