Map integer(id) to text string? - mysql

In my database, I'm storing a field called "type" as tinyInt, since I don't have that many types I decided not to create a table for storing the corresponding names of all these types.
When I query my table I want the types to be replaced by their corresponding names which I'm storing in php arrays. I wonder if there is a way to do this replacement inside the sql statement itself instead of looping through the results and do replacement on the returned result row.
Does something like this even exists:
select *, map(type, {1=>'abc', 2 => 'xyz'}) from orders

The only way (I'm fairly sure) to do this is using a CASE .. WHEN ... construct:
SELECT *, CASE type WHEN 1 THEN 'abc' WHEN 2 THEN 'xyz' END as stringType
FROM orders
You can also use the ELSE clause to specify a default, e.g. CASE type WHEN 1 THEN 'abc' WHEN 2 THEN 'xyz' ELSE 'unknown' END.

Actually, in contrast to the answer by mathematical.coffee, there actually is another way in MySQL to map a number to text - at least if your numbers are contiguous:
The ELT() function returns the N-th element of the list of strings: str1 if N = 1, str2 if N = 2, and so on. Returns NULL if N is less than 1 or greater than the number of arguments.
Example:
mysql> SELECT ELT(1, 'ej', 'Heja', 'hej', 'foo');
-> 'ej'
mysql> SELECT ELT(4, 'ej', 'Heja', 'hej', 'foo');
-> 'foo'
Source:
https://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_elt

Related

Create mysql view replacing a set of ints with corresponding strings

I have a mysql view where I store a set of values (hard,average,easy) as ints(3,2,1). Is there any way to create a mysql view (for my own analytics) that would replace the ints with their corresponding values?
How are you storing the values? I mean, if you just "know" that these are the values, then use case or elt():
select t.*, elt(val, 'easy', 'average', 'hard') as string_val
from t;
elt() happens to work because the values are sequential starting at 1. Otherwise, case is probably more appropriate.
you can use CASE:
select case col
when 1
then 'easy'
when 2
then 'average'
when 3
then 'hard'
end
from t

SQL - partially order by list of id's

I have a table which I query, the condition is something like id >10, and i want to add a condition based on id's from a list something like AND id IN(3,4,5,6,7).
I want to order the results by the id's from the list first.
can it be done?
In case you're using MySQL: (please tag questions appropriately)
From the manual:
FIELD(str,str1,str2,str3,...)
Returns the index (position) of str in the str1, str2, str3, ... list. Returns 0 if str is not found.
If all arguments to FIELD() are strings, all arguments are compared as strings. If all arguments are numbers, they are compared as numbers. Otherwise, the arguments are compared as double.
If str is NULL, the return value is 0 because NULL fails equality comparison with any value. FIELD() is the complement of ELT().
mysql> SELECT FIELD('ej', 'Hej', 'ej', 'Heja', 'hej', 'foo');
-> 2
mysql> SELECT FIELD('fo', 'Hej', 'ej', 'Heja', 'hej', 'foo');
-> 0
So you can easily do
SELECT
...
ORDER BY FIELD(id, 3,4,5,6,7), id

What is the difference between = and LIKE

I am running a query on a column postal (type double).
SELECT * FROM `table` WHERE `postal` LIKE 'abcdef'; # returns 1 record
and the same query using = returns 100+ records.
SELECT * FROM `table` WHERE `postal` = 'abcdef'; # returns 107 record
What could be the reason?
You are using LIKE on a DOUBLE field, you should not do that.
LIKE is reserved for pattern matching on strings. Use = for numbers, or convert your digit to a string first using CONVERT and then apply the logic with LIKE.
= compares two values for identity.
LIKE is for pattern matching ie. that is, it matches a string value against a pattern string containing wild-card characters.
Refer here
LIKE will check and return similar values where as = will check for the exact value.
The following things affects the result (not the complete list!)
Implicit conversation
MySQL extension to standard SQL's LIKE operator
In each cases an implicit conversion occours: MySQL tries to convert the values to a common data type. In the first case case 'abcdef' will be converted to double which results to 0. This is why you get 107 records when comparing with equals (=).
SELECT * FROM `table` WHERE `postal` = 'abcdef'; # returns 107 record
You should get exactly the same result by running
SELECT * FROM `table` WHERE `postal` = 0;
In MySQL, LIKE is permitted on numeric expressions. (This is an extension to the standard SQL LIKE.)
This means that SELECT CASE WHEN 10 LIKE '1%' THEN 1 ELSE 0 END is allowed and results to 1 (matched)
To be honest, I'm not sure which double value could match with LIKE operator with the pattern 'abcdef'.

MySQL In clause not giving the right result

In a MySQL table i have a field, containing this value for a given record : "1908,2315,2316"
Here is my sql Query :
SELECT * FROM mytable WHERE 2316 IN (myfield)
I got 0 results!
I tried this :
SELECT * FROM mytable WHERE 2315 IN (myfield)
Still 0 results
And then i tried this :
SELECT * FROM mytable WHERE 1908 IN (myfield)
Surprisingly i obtained the record when searching with 1908! What should i do to also obtain the record when searching with 2315 and 2316 ? What am i missing ?
Thanks
You appear to be storing comma delimited values in a field. This is bad, bad, bad. You should be using a junction table, with one row per value.
But, sometimes you are stuck with data in a particular structure. If so, MySQL provides the find_in_set() functions.
SELECT *
FROM mytable
WHERE find_in_set(2316, myfield) > 0;
You can't use IN() over comma separated list of no.s its better to normalize your structure first for now you can use find_in_set to find results matching with comma separated string
SELECT * FROM mytable WHERE find_in_set('1908',myfield) > 0
This question has been asked and answered before, but I don't want to hunt for it; this question should be closed as a duplicate. But, to answer your question:
The commas in the string, the column value, are just characters. Those are part of the string. They aren't seen as "separators" between values in the SQL text. The way SQL sees it, the column contains a single value, not a "list" of values.
So, in your query, the IN (field) is equivalent to an equals comparison. It's equivalent to comparing to a string. For example:
... WHERE 2316 = '1908,2315,2316'
And those aren't equal, so the row isn't returned. The "surprisingly" finding of a match, in the case of:
... WHERE 1908 IN ('1908,2315,2316')
that's explained because that string is being evaluated in a numeric context. That is, the comparison returns true, because all of these also true:
... WHERE 1908 = '1908,2315,2316' + 0
... WHERE 1908 = '1908xyz' + 0
... WHERE 1908 = '1907qrs' + 1
(When evaluated in a numeric context, a string gets converted to numeric. It just happens that the string evaluates to a numeric value that equals the integer value it's being comparing to.)
You may be able to make use of the MySQL FIND_IN_SET function. For example:
... WHERE FIND_IN_SET(2316,'1908,2315,2316')
But, please seriously reconsider the design of storing comma separated list. I recommend Bill Karwin's "SQL Antipatterns" book...
http://www.amazon.com/SQL-Antipatterns-Programming-Pragmatic-Programmers/dp/1934356557
In mysql IN clause is utilized as
SELECT * FROM mytable WHERE column_name IN (set_of_values) ;
Mention column name instead of values
Please try
SELECT * FROM mytable WHERE LOCATE(CONCAT (',', 2316 ','), CONCAT (',',myfield,',' ) ) <>0

Use comma separated list from one table as clause in query for another table

I have an events table with a field called breaks. This is populated with data in a comma separated format, i.e. 1,2,3 or 1 or 1,4,5 - the same format that MySQL's IN command uses.
I'd then like to run a query - on the slots table - to return all rows apart from those specified in events.breaks.
The query, theoretically, should be something like this:
SELECT
`slots`.`id` AS id,
RIGHT(`slots`.`time`, 8) AS `time`
FROM
`slots`, `event`
WHERE
`slots`.`id` NOT IN (`event`.`breaks`)
But that doesn't appear to work - if event.breaks is 4,5,7, the only row from the slots table that doesn't return is 4!
SQLFiddle here: http://sqlfiddle.com/#!2/913fe/1/0
You're passing a single field to the NOT IN () clause, not a subexpression. Think of it like this
(1, 2, 3)
is roughly the same as
SELECT 1
UNION
SELECT 2
UNION
SELECT 3;
as a subexpression. What you're doing instead is
('4,5,7')
which is roughly equivalent to
SELECT '4,5,7';
which in turn MySQL probably converted to a number for the comparison and the result is
NOT IN (4)
What you're actually trying to do isn't really supposed to be done like that. It'd be better if you added an AxB relation table so you can select several rows with the IDs you don't want.
Give this a try:
SELECT slots.id AS id, RIGHT(slots.time, 8) time
FROM slots, event
WHERE FIND_IN_SET(slots.id, event.breaks) = 0
This is how the FIND_IN_SET(str,strlist) function works:
Returns a value in the range of 1 to N if the string str is in the string list strlist consisting of N substrings. A string list is a string composed of substrings separated by “,” characters. [...] Returns 0 if str is not in strlist or if strlist is the empty string.
Also note that IN (val1, val2, val3) is NOT the same as IN (val4) where val4 is a commma-separated string. The IN clause will compare by equality.
you may need a subselect to return the split string
... NOT IN (SELECT your_split_fnc(`event`.`breaks`) FROM `events`)
See answers here for a way to split strings in MySQL Can Mysql Split a column?
instr() MySQL function could be of help also
... INSTR(event.breaks,id) = 0
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_instr