Should I input 2 digit numbers in TINYINT? - mysql

I need a column that can store values from -1 (negative 1) to +15 (positive 15).
I am using a TINYINT(2) column at the moment and it works just fine.
I need to know if this is ideal and safe to use because I read online that TINYINT should ONLY be used for binary value such as 1 and 0.

MySQL TINYINT has a storage of 1 byte.
Unsigned value in range 0 to 255 or signed value of range -128 to 127 can be stored.
Also note that 2 in TINYINT(2) does not limit the length of your value to 2 digits. more info in this link - What is the size of column of int(11) in mysql in bytes?

TINYINT will store your value perfectly fine; however it will not guarantee that it will be correct (exactly in that range). Please note that (2) is just related to padding when selecting via the command line and it has nothing to do with the limit. Normally you do not need it at all. TINYINT (signed) will store values from -128 to 127 regardless if you add (n) at the end of it or not.
If you want to make sure that the value is always exactly in the range between -1 and 15, you have two options:
Using CHECK constraint over the existing TINYINT (recommended solution):
CREATE TABLE tiexample(
val TINYINT NOT NULL,
CONSTRAINT val_range CHECK(val>=-1 AND val<=15)
);
INSERT INTO tiexample(val) VALUES(122);
ERROR 4025 (23000): CONSTRAINT `val_range` failed for `test`.`tiexample`
INSERT INTO tiexample(val) VALUES(12);
Query OK, 1 row affected (0.001 sec)
Listing all values as ENUM:
CREATE TABLE tiexample(
val ENUM("-1","0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15") NOT NULL,
CONSTRAINT val_range CHECK(val<>"")
);
INSERT INTO tiexample(val) VALUES("122");
ERROR 4025 (23000): CONSTRAINT `val_range` failed for `test`.`tiexample`
INSERT INTO tiexample(val) VALUES("12");
Query OK, 1 row affected (0.001 sec)
Note that using this method you must insert the values with quotes (like "8", not just 8). Also first it may seem strange that we use CHECK constraint here but it is needed because by default inserting invalid value on ENUM field will insert an empty string in the table (not NULL - it's an empty string). You can get around that issue and skip the CHECK constraint if you use this sql mode before inserting into the table:
SET SQL_MODE = 'Traditional';
Personally I would not struggle with the ENUM type. It is more suitable for different use-cases.
Footnote: CHECK constraints are available and working only in MySQL MySQL 8.0.16+ and MariaDB 10.2.1+. Prior versions accept the constraint but never use it (it will ignore them).

Related

MySQL WHERE Condition on integer field returning incorrect values

I'm having a problem with MySQL returning the incorrect result when applying a WHERE condition to an integer field with a string value.
CREATE TABLE `people` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
INSERT INTO `people` (`id`, `name`)
VALUES
(1, 'Bob'),
(2, 'Sally'),
(3, 'Jim');
Now when I run the query:
SELECT *
FROM people
WHERE id = '1-abcd';
My result set is:
id name
1 Bob
MySQL appears to be truncating the string value '1-abcd' to '1' behind the scenes as soon as it hits a non-integral character (in the conversion from a string to INT).
You're probably wondering why this matters. I'm trying to fix a site for a PCI compliance scan. The scan thinks the URI '/some/page?id=102-1' is allowing some form of sequel injection, but in reality it's showing the same content at '/some/page?id=102'.
This is not an issue in one place. It is an issue all over the place, and it's a fairly large system. Is there some way to rectify this on the MySQL end of things, so it no longer mistakenly judges the two values to be equivalent? I looked at the documentation for SQL modes, but didn't see anything regarding this circumstance.
UPDATE: I filed a dispute with the company that produced the scan, which they accepted, so I'm no longer in the woods. But it is disappointing that there's apparently no way to configure the casting behavior of MySQL from a string to INT in this case. (You can, but only for INSERTs and UPDATEs.)
What happens that MySQL type-casts the string literal value to an integer, and when it does that it starts from the left of the string and as soon as it reaches a character that cannot be considered part of a number, it strips out everything from that point on. So 1-0 gives output matching to 1. To do this you can use cast. I am not 100% sure about the syntax but it is like this:
select * from people
where id =
(
case when ISNUMERIC( '1-0' )
then cast ('1-0' as int)
else null
end )
What this will do is that if it is an numeric value then it will return the correct matching row or else not.
Edit:
The above query seems to be of MSSQL/Oracle and would not work with MySQL. For MySQL you can use RegExp. I have never use one but you can find more details here:
http://mysqlhints.blogspot.in/2012/01/how-to-find-out-if-entire-string-is.html
http://www.ash.burton.fm/blogs/2010/12/quick-tip-mysql-equivalent-of-isnumeric
http://www.justskins.com/forums/how-to-use-isnumeric-137604.html

Adding record in MySQL using forward engineering and there was an error for inserting in Numeric Data Type

First lets see how my table structure is set:
CREATE TABLE IF NOT EXISTS `RFVPOS`.`Station` (
`id` INT NOT NULL AUTO_INCREMENT,
`code` VARCHAR(20) NOT NULL,
`name` VARCHAR(45) NOT NULL,
`safeDropAmount` DECIMAL(4,4) NOT NULL,
`deadStockVolume` DECIMAL(4,4) NOT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB
And here is what MySQL forward engineering does:
`INSERT INTO `rfvpos`.`station` (`code`, `name`, `safeDropAmount`, `deadStockVolume`) VALUES ('test', 'test',` '100', '300');
Im aware that by enclosing my safeDropAmount and deadStockVolume with single qoutes would mean they are in a varchar datatype, so im removing them like this:
`INSERT INTO `rfvpos`.`station` (`code`, `name`, `safeDropAmount`, `deadStockVolume`) VALUES ('test', 'test', 100, 300);
But either ways, when i execute these scripts they say the same thing. this:
ERROR 1264: 1264: Out of range value for column 'safeDropAmount' at row 1
Kindly help me. I was using MySQL before, and after 3 years i'm back again at it, so i'm kinda refreshing.
Best regards,
The column datatype DECIMAL(4,4) won't allow for a value larger than 0.9999 to be stored. Attempting to store value greater than that (for example, 300) would give the "out of range" error.
DECIMAL(4,4) specifies a total of four digits, with four of them (all of them) after the decimal point, leaving zero digits before the decimal point.
If we want to allow values up to 9999.9999, we'd want datatype DECIMAL(8,4).
That's a total of eight digits, with four of those digits after the decimal point, the remainder (8-4) before the decimal point.
Also, MySQL will evaluate the values in single quotes as numeric, in a numeric context, such as inserting into DECIMAL column. The addition or removal of the single quotes around the numeric values, e.g. 300 or '300', has no effect, and they will be evaluated the same. (The difference is what error is returned, if we have a value that isn't numeric (invalid column vs .re is a difference in the error returned what we specify as a literal is not a numeric... foo would raise "Unknown column" error, 'foo' would raise "Incorrect value" error.

Why does TINYINT(1) function as a boolean but INT(1) does not?

Why does TINYINT(1) work as a boolean? The way I understood the official docs, the (1) should mean it has a display width of 1, so if I store 56 in it, I thought it should print 5. But for some reason it always prints either 1 or 0.
And another case is if I store 56 in an INT(1), then it prints 56 (at least according to SQLFiddle). What's going on here?
The (1) in parentheses for a MySQL integer type has nothing to do with the range of values accepted by the data type, or how it is stored. It's only for display.
See also my answer to Types in MySQL: BigInt(20) vs Int(20) etc.
TINYINT is no different from TINYINT(1) or TINYINT(2) or TINYINT(64). It's an 8-bit signed integer data type, and it accepts any 8-bit integer value from -128 to 127.
mysql> create table b (i tinyint(1));
mysql> insert into b values (42);
mysql> select * from b;
+------+
| i |
+------+
| 42 |
+------+
For convenience, MySQL supports an alias for BOOL, which is replaced immediately by TINYINT(1).
mysql> create table b2 (i bool);
mysql> show create table b2;
CREATE TABLE `b2` (
`i` tinyint(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
As I said, the use of (1) means almost nothing, it's only a convention so that if you see TINYINT(1) it's reasonable to assume the column is intended to be used as a boolean. But nothing in MySQL prevents you from storing other integer values in it.
If you want a column to accept only 0 or 1, you can use BIT(1):
mysql> create table b3 (i bit(1));
mysql> insert into b3 values (0), (1);
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> insert into b3 values (-1);
ERROR 1406 (22001): Data too long for column 'i' at row 1
mysql> insert into b3 values (2);
ERROR 1406 (22001): Data too long for column 'i' at row 1
This doesn't save any space compared to TINYINT though, because the storage for a given column rounds up to the nearest byte.
PS: Despite answer from #samdy1, TINYINT does not store strings '0' or '1' at all, it stores integers 0 or 1, as well as other integers from -128 to 127. There is no need to quote integers in SQL, and I am often puzzled why so many developers do.
TINYINT columns can store numbers from -128 to 127.
TINYINT(1) is a bit weird though. It is (perhaps because it is supposed to act as a BOOLEAN datatype), returns only 0 and 1 in some context, while it still keeps the stored (-128 to 127) values.
(Correction: I only see this weird behaviour in SQL-Fiddle and not when accessing MySQL locally so it may well be a SQL-Fiddle quirkiness, possibly related to the quivalence with BOOLEAN) and not a MySQL problem.
See the SQL-Fiddle
CREATE TABLE test
( i TINYINT(1)
) ;
INSERT INTO test
(i)
VALUES
(0), (1), (6), (120), (-1) ;
Where we get (only in SQL-Fiddle, not if we access MySQL otherwise!):
SELECT i
FROM test ;
i
-----
0
1
1
1
1
but:
SELECT CAST(i AS SIGNED) i2
FROM test ;
i2
-----
0
1
6
120
-1
This is a mysql Jdbc configuration subject.
You can config mysql jdbc to convert TinyInt(1) to Boolean or Integer through set jdbc url config property "tinyInt1isBit" to "true" (default) or "false".
from: https://dev.mysql.com/doc/connector-j/en/connector-j-reference-configuration-properties.html
Should the driver treat the datatype TINYINT(1) as the BIT type (because the server silently converts BIT -> TINYINT(1) when creating tables)?
Default: true
The engine is smart enough to know that TINYINT(1) and BOOL are the same. However INT(1) only affects the Display Width instead of the underlying storage size. Display width only comes into play when the value is less width then the display width. Then it gets padded.
http://alexander.kirk.at/2007/08/24/what-does-size-in-intsize-of-mysql-mean/
As I understand it, TINYINT(1) can only hold '0' or '1' (from own experience).
Thus, one can assume that the '0' or '1' is translated into true or false.

Why is my MySQL unique key failing?

INSERT INTO `ree`.`media`
(`CREATEDATE`, `FILETYPE`, `MIMETYPE`, `MLSNUMBER`, `MODIFYDATE`, `POSITION`, `URL`) VALUES
('2011-12-27T15:00:16', 'PRIMARY PHOTO', 'image/jpeg', 5030011414, '2011-12-27T15:00:16', 1, 'http://image.realcomponline.com/photos.rps?PATH=PROPERTY/57FA/57FAA44C48854C/3QQGONGA03I7CN.jpg&g=100&sp=0&l=0&t=0&r=10000&b=10000&o=0&1cf=0&w=320&h=240'),
('2011-12-27T15:00:18', 'PRIMARY PHOTO', 'image/jpeg', 5030011507, '2011-12-27T15:00:18', 1, 'http://image.realcomponline.com/photos.rps?PATH=PROPERTY/6FC7/6FC7B6F88D8F45/3SQGONGA01RXH1.jpg&g=100&sp=0&l=0&t=0&r=10000&b=10000&o=0&1cf=0&w=320&h=240')
Error: Duplicate entry '2147483647-1' for key 'uneek'
It seems like my UNIQUE key of MLSNUMBER isn't parsing the entire number before differentiating the two inserts.
Both start with 5030011...
Here is how I build my the key:
ADD UNIQUE uneek ( MLSNUMBER , POSITION )
Is there a way to build this key so it accepts the entire 10 digits instead of the first 7 digits?
Thanks in advance!
You have run out of range of int.
2147483647 = 2^31 - 1
You might change this int on that field to be e.g. 64bit:
ALTER TABLE media MODIFY COLUMN MLSNUMBER BIGINT NOT NULL;
(Modify unsigned and not null for your needs).
It seems that you've defined the MLSNUMBER column as an Integer type, and the large values are getting truncated to the largest 32 bit signed value, which is 2147483647.
I verified this by attempting to add the value 5030011507 to an Int column, and it ended up storing the value 2147483647. That matches the number in your error message. I also received a warning when the value was truncated.
You can try changing the column type to BIGINT, which will allow values up to 9223372036854775807.

Query update issue

I m just confuse in this Query and i don't know how to solve this if you have any idea about this please help me or helps are definitely appreciated
I have table structure like this and test column contain 3 value
UPDATE `test` SET test = test -3
when i execute this Query the result will be show like this
UPDATE `test` SET test = test -4
But when i execute this query the result will not proper save in test column like this
0 result required or i don't need any subtract value also
Apparently you are using the BIGINT UNSIGNED data type. If you want to store negative numbers, you need to change it to a regular signed BIGINT (Also be sure to set it to NULL or NOT NULL as required):
ALTER TABLE test
MODIFY COLUMN test BIGINT;
UPDATE: If you actually want to store 0 instead of -4 in your example, you can do so using the GREATEST() function like this:
UPDATE `test` SET test = GREATEST(CAST(test AS SIGNED) - 4,0)
The problem is most likely due to the fact that your bigint is unsigned.
Per the documentation, an unsigned bigint is
A large integer. The signed range is -9223372036854775808 to
9223372036854775807. The unsigned range is 0 to 18446744073709551615.
Notice the unsigned range and how it is your number - 3 (4 comes from getting from 0 to xxx15 I believe)
So, you should only need to update your column to be a bigint that is not unsigned (signed) and this should work.
ALTER TABLE test MODIFY COLUMN test BIGINT SIGNED;
UPDATE
If you want to keep the BIGINT UNSIGNED so that you cannot have negative numbers, then you could write a trigger to force a 0, or you could just make your query something like this:
UPDATE test
SET test = CASE WHEN test >= value THEN test-value ELSE 0 END
Basically, if the value attempting to be subtracts is more than the current value, then just set the value to 0, otherwise perform the subtraction.
As per this question: MySQL: bigint Vs int
bigint's maximum value is 18,446,744,073,709,551,615
You're wrapping around to the highest value when you subtract from 0, since your bigint is unsigned.