ISSUE: Mysql converting Enum to Int - mysql

I have a very simple rating system in my database where each rating is stored as an enum('1','-1'). To calculate the total I tried using this statement:
SELECT SUM(CONVERT(rating, SIGNED)) as value from table WHERE _id = 1
This works fine for the positive 1 but for some reason the -1 are parsed out to 2's.
Can anyone help or offer incite?
Or should I give up and just change the column to a SIGNED INT(1)?

this is what you want
select enum+0 as enum

This conversion to int in MySQL for enum is only possible:
CAST(CAST(`rating` AS CHAR) AS SIGNED) as value from table WHERE _id = 1

Yes, I'd suggest to change the type of the column. The issue becomes clear when you read the doc about enum type (which strongly recommends not to use numbers as enumeration values!) - the index of the enum item is returned, not the enum value itself.

Ok guys,
Just had a bit of a mere of a time with this one. I learned that i shouldn't use ENUMs where integers are the values. However We had years worth of data and i couldn't alter the database.
This bad boy worked (turning it into a character, then into a signed int).
CAST(CAST(`rating` AS CHAR) AS SIGNED) as value from table WHERE _id = 1

use
SELECT SUM( IF( columnname >0, CAST( columnname AS CHAR ) , NULL ) ) AS vals
FROM `tableName`

I wouldn't use enum here too, but it is still possible in this case to get what is needed
Creating table:
CREATE TABLE test (
_id INT PRIMARY KEY,
rating ENUM('1', '-1')
);
Filling table:
INSERT INTO test VALUES(1, "1"), (2, "1"), (3, "-1"), (4, "-1"), (5, "-1");
Performing math operations on enums converts them to indexes, so it is possible just to scale the result value:
SELECT
SUM(3 - rating * 2)
FROM
test;
Result: -1 which is true for the test case.

Related

How to check whether integer contains 0 in MySQL?

Im trying to pull bunch of records from MySQL with condition whether the column value contains 0. If I convert to string and check with contains (%0%), it takes more time to execute.. Is there any short way to check on Integer column? Thank you..
You can probably run a regex on the integer column. Something like below.
SELECT fieldname FROM tablename WHERE fieldname REGEX '[0]';
About the performance, I believe that LIKE is faster than REGEX. But since your LIKE involves string conversion, this would have some difference in execution time
#SriniK You can use either POSITION() or LOCATE() in conjunction with CONVERT() to convert the column value into a string and then find if the string "0" exists within the string. Both of these functions return a positive number greater than 0 indicating the index of the substring within your string and they return a 0 if the string isn't found.
Here's how to create a simple test table to illustrate:
CREATE TABLE `testTable` (`id` INTEGER NOT NULL AUTO_INCREMENT, `someNum` INTEGER, PRIMARY KEY(`id`));
INSERT INTO `testTable` (`someNum`)
VALUES
(1),
(10),
(11),
(100),
(111),
(222),
(210);
And here are two queries illustrating both POSITION() and LOCATE():
SELECT `id`,
`someNum`,
POSITION('0' IN CONVERT(`someNum`, CHAR))
FROM `testTable`
WHERE POSITION('0' IN CONVERT(`someNum`, CHAR)) > 0
;
SELECT `id`,
`someNum`,
LOCATE('0', CONVERT(`someNum`, CHAR))
FROM `testTable`
WHERE LOCATE('0', CONVERT(`someNum`, CHAR)) > 0
;
I have mocked this up on dbfiddle here so you can get a better look at it.
If this doesn't get you what you need or if you don't understand something please leave a comment with details and I'll do what I can to help further. Good luck.

Is there a function in MySQL to not allow the selection of a value directly after a value already selected from an ENUM data type in the database?

Is there code to not allow a value directly after a value that's already saved be inserted into the table. The field is an ENUM.
Cant find code anywhere
None available for ENUM
Timeslot ENUM('09:00','09:30','10:00')
09:00 saved already
'09:30' shouldn't be allowed to be inserted into the table
'10:00' should insert fine
There is no default function that I am aware of to do what you are wanting.
I think you will have to do some checks using the ENUM index. ENUM values are mapped to a numeric index. You can select column_name+0 on an ENUM column and that will give you the index value of the ENUM rather than the ENUM value. MySQL ENUM Doc
In your case the ENUM index would look something like this:
NULL -> NULL
0 -> ''
1 -> '9:00'
2 -> '9:30'
3 -> '10:00'
For instance, if you have 1 record with Timeslot set to '9:00' and you 'SELECT TimeSlot+0 FROM table' your result for the record will be 1. If the column value was '9:30' the index would be 2, etc.
You can find the potential index of an incoming value using something like this:
SELECT FIND_IN_SET('new_value', REPLACE(SUBSTRING(column_type,6, LENGTH(column_type) - 6), '\'', '') ) AS enum_options
FROM information_schema.columns
WHERE column_name='your_enum_column'
AND table_schema = 'your_schema';
If the result of this is equal to any of the index values (or index value +1) of any of the values already in the table, you do not want to allow this new entry. You can use the above query as a subquery inside a case statement to compare this new value's index to your previous values' indexes.
EDIT (4/2/2019):
After a couple of comments I think that the following may get you closer to what you need. I have not been able to test this query out, but it should be close.
CREATE TEMPORARY TABLE booking_conflicts AS (
SELECT MAX(
IF(
FIND_IN_SET(
(SELECT FIND_IN_SET('12:00', REPLACE(SUBSTRING(column_type,6, LENGTH(column_type) - 6), '\'', '') )
FROM information_schema.columns
WHERE column_name='your_enum_column'
AND table_name = 'booking'
AND table_schema = 'your_schema'),
CONCAT(time_slot+0, ',', time_slot+1)
) > 0,
1,
0) AS is_time_conflict
FROM booking
WHERE facility_id = 6
AND booking_date = '2020-07-04'
);
INSERT INTO bookings
(facility_id,booking_date,time_slot,member_id)
VALUES (6,'2020-07-04','12:00',2)
WHERE (SELECT is_time_conflict FROM booking_conflicts) = 0;
What this is doing is getting all used time_slots from that date for that facility and comparing them with the new time slot you are trying to use. If the new time slot's index is equal to the index of a previously used time_slot or of a previously used time_slot + 1, then the query will return 1, otherwise 0. We store that in a temp table and access the temp table from the insert.

mysql CONVERT() and CAST() to integer are increasing the value by 1

I'm trying to select records from a phone-call table where the value of an ENUM string field called Call_Rating is less than the integer value 4. The Call_Rating field can only contain the values '0','1','2','3','4','5'. Whenever I use CONVERT(Call_Rating, UNSIGNED INTEGER) or CAST(Call_Rating AS UNSIGNED), the values of the Call_Rating field are increased by 1. Why is it doing this and is there a way to avoid it other than just manually subtracting 1 from the CALL() or CAST() functions?
Also, this is an old DB that was set-up by someone else and is still in use by various systems, so some way of getting around this issue without changing the DB schema would be useful.
Create Table
CREATE TABLE `member_calls` (
`CallID` int(10) NOT NULL AUTO_INCREMENT,
`Call_Rating` enum('0','1','2','3','4','5') CHARACTER SET latin1 NOT NULL DEFAULT '0',
PRIMARY KEY (`CallID`)
) ENGINE=MyISAM AUTO_INCREMENT=538616 DEFAULT CHARSET=utf8;
Data in Table
INSERT INTO `member_calls`
(`CallID`, `Call_Rating`)
VALUES
(510515, '4'),
(510909, '0'),
(538614, '3'),
(538615, '5');
Select Statement
SELECT `CallID`, `Call_Rating`, CAST(`Call_Rating` AS UNSIGNED) AS 'Casted', CONVERT(`Call_Rating`, UNSIGNED INTEGER) AS 'Converted'
FROM `member_calls`
WHERE CONVERT(`Call_Rating`, SIGNED INTEGER) < 4
OR CAST(`Call_Rating` AS UNSIGNED) < 4;
Given Results
CallID Call_Rating Casted Converted
510909 0 1 1
Expected Results
CallID Call_Rating Casted Converted
510909 0 0 0
538614 3 3 3
Edit 2016-5-17
Thank you everyone for your input. I now understand why the issue was happening. Basically the ENUM was being treated like an array and CAST()/CONVERT() were returning the index of the array rather than the value. The best solution to this would be to change the ENUM to an INT field, but that is not desirable in my situation since the DB is being used live, and altering the data type could cause issues elsewhere. For that reason, lserni's solution was the most useful for me.
The direct conversion of ENUM to INTEGER yields the index of that ENUM, and since they start from 1, the first element is 0 and becomes 1, etc.
It's not increasing by 1 at all: it's returning an integer value that by chance seems as it's the correct value plus 1. But it could be anything else. If you had an enum of '1','2','3','4','5', without the '0', then the result would appear to be correct, even if it really isn't.
Either run a double convert passing from CHAR, or an implicit convert again after converting to CHAR:
SELECT CONVERT(CONVERT(Call_Rating, CHAR(1)), UNSIGNED), 0+CONVERT(Call_Rating, CHAR(1)), 0+Call_Rating, CAST(Call_Rating AS UNSIGNED) from member_calls;
+--------------------------------------------------+---------------------------------+---------------+-------------------------------+
| CONVERT(CONVERT(Call_Rating, CHAR(1)), UNSIGNED) | 0+CONVERT(Call_Rating, CHAR(1)) | 0+Call_Rating | CAST(Call_Rating AS UNSIGNED) |
+--------------------------------------------------+---------------------------------+---------------+-------------------------------+
| 2 | 2 | 3 | 3 |
+--------------------------------------------------+---------------------------------+---------------+-------------------------------+
I can't give a detailed explanation of what is happening here, but it's likely to do with the fact that you are using an ENUM with numeric values, instead of an INT type, which seems like the sane choice.
The mySQL manual strongly recommends against this:
We strongly recommend that you do not use numbers as enumeration values, because it does not save on storage over the appropriate TINYINT or SMALLINT type, and it is easy to mix up the strings and the underlying number values (which might not be the same) if you quote the ENUM values incorrectly. If you do use a number as an enumeration value, always enclose it in quotation marks. If the quotation marks are omitted, the number is regarded as an index. See Handling of Enumeration Literals to see how even a quoted number could be mistakenly used as a numeric index value.
mySQL is probably using the numeric index instead of the ENUM value and that's causing the weirdness.
Just switch to a proper INT field.
You should get that ENUM is not string or integer that is enum.
To me that is the type you should avoid in DB as much as possible.
Here is fiddle tha illustrate why that happens to you:
http://sqlfiddle.com/#!9/de53b2/1
as you can see when mysql casting ENUM to int - that returns something like index of saved value in array(enum) but not the value casted to int.
But just to illustrate mysql power functionality you can run this query to get expected result:
http://sqlfiddle.com/#!9/e2cf5/3
SELECT `CallID`, `Call_Rating`,
ELT(CAST(`Call_Rating` AS UNSIGNED),'0','1','2','3','4','5') AS 'Casted',
ELT(CONVERT(`Call_Rating`, SIGNED INTEGER),'0','1','2','3','4','5') AS 'Converted'
FROM `member_calls`
HAVING `Casted` < 4
OR `Converted` < 4;
But anyway that is not best solution.
I think you should better redesign your db schema.

Converting VARCHAR to DECIMAL values in MySql

I have imported a CSV file that contains string values (eg.eating) and floating values (eg. 0.87) into a table in my phpMyAdmin database. After I get ride of all the string values and retain only the rows that have the decimal values, I need to convert such values from VARCHAR to DECIMAL/FLOAT so that I can perform a MAX() on this attribute.
How do I do this? Each time I try doing this through the GUI in phpMyAdmin, all my values are automatically rounded off to 0 and 1s.
Please help me!
Without Converting you can find Maximum using this query
select max(cast(stuff as decimal(5,2))) as mySum from test;
check this SQLfiddle
your demo table:
create table test (
name varchar(15),
stuff varchar(10)
);
insert into test (name, stuff) values ('one','32.43');
insert into test (name, stuff) values ('two','43.33');
insert into test (name, stuff) values ('three','23.22');
Your Query:
For SQL Server, you can use:
select max(cast(stuff as decimal(5,2))) as mySum from test;
Be aware that if you convert from VARCHAR to DECIMAL and do not specify a precicision and maximum number of digits (i.e. DECIMAL instead of DECIMAL(5,2)) MySQL will automatically round your decimals to integer values.
I think you need to try doing something like this on your MySQL if you have admin privilege on your MySQL.
ALTER TABLE tablename MODIFY columnname DECIMAL(M,D)
for the M,D variables, read this - http://dev.mysql.com/doc/refman/5.0/en/fixed-point-types.html
And MySQL should be able to automatically converting a text to a numeric. Just that the data type in MySQL might not be a decimal yet that's why you can't store any decimal.
Hope it may help someone
select convert( if( listPrice REGEXP '^[0-9]+$', listPrice, '0' ), DECIMAL(15, 3) ) from MyProduct WHERE 1

How to select value number of ENUM types in MySql?

I need to select a row from table below, but the problem is the value in $row['city'] is the textual represent of the value, and i need its number(Toronto = 2). (Same as when we INSERT INTO, and we use value number instead of text)
Requests Table Structure:
req_id INT
uname VARCHAR(30)
city ENUM('New York', 'Toronto', 'Las Vegas')
You just need to force your city into a numeric context, from the fine manual:
If you retrieve an ENUM value in a numeric context, the column value's index is returned. For example, you can retrieve numeric values from an ENUM column like this:
mysql> SELECT enum_col+0 FROM tbl_name;
So you want this sort of thing:
select req_id, city+0
from your_table
where city = 'Toronto'
BTW, you can insert an enum using either the string or integer representation.
You can use the CAST function. The documentation doesn't mention this specific use case, but it works as expected. I prefer it because it looks elegant and clear:
SELECT CAST(city AS UNSIGNED) FROM your_table;