Mysql compare comma-separated field with single string - mysql

So a field called schools in the database might have a value of:
'13,121,112,1212'
I'm using that to show the potential for a mistake.
Suppose I'm looking for a value of 12 in that field. The commas denote a "whole number" and I don't want to match 112 or 1212
Is there a more elegant match than this?
#compare = 12;
WHERE CONCAT(schools,',') LIKE CONCAT('%',compare,',%)
I was recently impressed by the GROUP_CONCAT function but this is kind of in reverse of that. Thanks!

For this simple case you can use FIND_IN_SET();
WHERE FIND_IN_SET('13', schools);
Note though that there is no good indexing for columns with comma separated text, so the queries will be much slower than a normalized database.

Related

SQL RegEx to handle comma separated IDs

I have a string that denotes which users are allowed to access something. For instance, if user 1, user 2, and user 3 could access it, the accessibility column would contain 1,2,3. If only user 1 could access it, it would only be 1 and so forth.
I know I can't do a simple CONTAINS clause because searching for 1 could return true for 14,2,3. How would I get a regex to accommodate when there is a comma on both sides, on one side, or neither of the ID number?
Here is a sample of what I'm trying to do
DataID: 1
Accessibility: "1,2,3,4,5"
Data: "secret stuff"
DataID: 2
Accessibility: "5,6,7,8,9"
Data: "more secret stuff"
I need to tell the regex to search for a number and to make sure its at the beginning of the string and the end of the string if it has no commas around it, is at the beginning of the string if it only has a comma after it, is at the end of a string if it only has a comma before it, or if it commas on both sides that's fine because it's in the middle of the string.
I know what I need to do, but don't know how to achieve it. Thanks.
First, you have a really bad data structure for several reasons:
The proper way to store lists in SQL is using tables, not strings.
The proper way to store integers in SQL is as integers, not strings.
Ids should be defined with a proper foreign key relationship, which you cannot do when the id is stored in a string.
Sometimes, we are stuck with other people's bad design decisions. That is, we are unable to create a proper junction table, with one column for the DataId and each user who has access to it.
In that situation, you can use the find_in_set() functionality in MySQL. This does not require a regular expression. You can just write:
where find_in_set($user, accessibility) > 0
Since A-Z, 0-9, and underscore are considered word boundaries, you could generalize like this:
-- word-bound DataID, e.g. 1 becomes \b1\b
SELECT '\b' || DataID || '\b' AS DataID_Bound FROM USER
WHERE REGEX_LIKE(DataID_Bound, Accessibility)
That way it doesn't matter if there is a comma leading, trailing, or if it's a sole occupant of the search subject. But it deffinitely cannot match 14 or 21, etc. \b1\b will only match solo 1, \b14\b will only match whole word 14, etc.

How do I assign a variable to each letter of a string in MySQL?

I am trying to figure out a way of doing an "anagram" function as a stored procedure on MySQL. Lets say I have a database containing all the words in the dictionary - I want to enter a parameter of some letters as a VARCHAR and get back a list of words which make up an anagram of those letters.
I guess what I'm sort of saying is, how do I run an SQL command to say "Select all words which are the same length as the parameter AND contain each of the letters in the parameter".
I have explored the string functions available (http://www.hscripts.com/tutorials/mysql/string-function.php). I'm sure these can be used in conjunction in some way but can't quite get the syntax right when it gets complicated.
I am new to SQL, and it just seems like the String functions available are very limited. Any help would be greatly appreciated :)
You don't; it's not a sensible thing to ask a relational database to do.
However, if someone was forcing me at gunpoint to implement anagram finding using a relational database, I would denormalize it like this:
word | sorted
-----|-------
bar | abr
bra | abr
keel | eekl
leek | eekl
Where "sorted" consists of all of the letters in "word", sorted using any rule you like as long as it's a total order. You would use something other than SQL to compute that part.
Then you could find anagrams with something like this:
SELECT w2.word AS anagram
FROM words w1
JOIN words w2 ON w1.sorted=w2.sorted
WHERE w1.word = 'leek'
AND w2.word <> w1.word
SQL is probably not the right place to do this, you should do it on the front end.
First of all consider the properties of an anagram, it will be the same length as the words in your dictionary. You can start by retrieving those words.
Instead of creating a variable per letter consider using an array
Each letter maps to an index (a=0, b=3, etc...). Each time you run into that letter increase the value for that bucket so for the word "dad" you'll end up with a structure that looks like this:
arr[0]=1, arr[1]=0, arr[2]=0, arr[3]=2, arr[4]=0 and so on...
Now you can just see if your words match each item in the array.
While not impossible in SQL, you can represent that kind of logic in the database, for example another table that will have a reference to the dictionary word and each tuple would be the array, then you can just retrieve all the items with the same values.

What should be the Table structure for following situation

I have a situation where i need to store "Error Type" which has following options
I want to know what is the best way I should use to create my table "Error".
So either I take a "VARCHAR" data type and store values like "1,3,4" (Comma Separated) if "Take Off, Details and Legend" is selected and parse it when getting in view
OR
Take separate column for each field in table with data type "TINYINT" like "IsTakeOff" , "IsSpecifications" ,"Details" etc.
Please advice
Thanks
If user with id 1 select 1,3 and 4 then you can use following
Don't use multiple columns unless you are very confident the number won't grow.
Otherwise use a many-to-many association table - one with columns for the id of the item and the id of the error
One way to go might be to do it as an int and then in your code do something like this PHP, I don't know language you are using but most languages have a switch so it shouldn't be to hard to translate to another language.
$row // The row from the database.
switch ($row['Error_Type'])
{
case ('0'):
{
// Do something.
break;
}
...
}
You can use comma seperated column here that is varchar because you know that no of records will not grow here. And this is limited to only 5 values. And while searching the field you can use MySQL FIND_IN_SET which is very effective for this kind of situations. FIND_IN_SET will take two parameters. Your search keyword and the comma seperated string.

Mysql searching records containing wildcards

I have a table with tens of thousands of VIN numbers. Many of them look along the lines of this:
6MMTL#A423T######
WVWZZZ3BZ1?######
MPATFS27H??######
SCA2D680?7UH#####
SAJAC871?68H06###
The # represents a digit and the ? a letter (A-Z).
I want to search for the following: 6MMTL8A423T000000.
I am struggling to work out the logic. Should I use a function? Should I use mysql regex?
A regular expression match would be a good way to approach this problem. What you need to do is convert the vin expressions into valid regular expressions that represent the logic you've indicated. Here's a simple way to do that:
replace(replace(vin,'#','[0-9]'),'?','[A-Z]')
This would convert 6MMTL#A423T###### into 6MMTL[0-9]A423T[0-9][0-9][0-9][0-9][0-9][0-9]. Now using this converted format, do a regular expression match query:
select vin
from vins
where '6MMTL8A423T000000' regexp replace(replace(vin,'#','[0-9]'),'?','[A-Z]')
Sample Output: 6MMTL#A423T######
Demo: http://www.sqlfiddle.com/#!2/ee4de/4

How to order text that contains double colons (::)

To order by name I'm using 'order by name'
But the names contain double colons : '::'
How can I order by the text that occurs subsequent to the double colons ?
So :
aaaa::bbbb
aaaa::aaaa
aaaa::1234
aaaa::a1234
Will be ordered :
aaaa::1234
aaaa::aaaa
aaaa::a1234
aaaa::bbbb
Order by the substring ans use locate to find where it starts:
order by substring(name, locate('::', name) + 3, 30)
It'll decrease performance since no index will be used.
You would have to create a new field in MySQL then insert the second part of your text into it. Sort by uses various indexes and algorithms (such as divide and conquer).
As such it would not work on sorting on a specific portion of a specific string, and if you did manage to 'fake' a way of doing it, the performance would be terrible due to lack of indexes.
Sorry, I realise this probably isn't the answer your looking for, but I'm afraid the best way is the slightly longer way, but at least you can then do it at lighting fast speeds if you add an index to it :)
You must split the text into two columns and order by the latter one. You can either split and join the columns in application code or use views and stored procedures to make it look like one column to a database client.
about your sorting , according to ascii values numbers come first before alphabets,
so aaaa:1234 should come first
You can retrieve the values and sort in PHP
Navsort
<?php
$arr = array("aaaa::bbbb","aaaa::aaaa","aaaa::1234","aaaa::a1234");
$sec=$arr;
natsort($sec);
print_r ($sec);
?>
You may try the following approach
Get all records where All data is Alphabet after ::
UNION
Get all records where All data is Numeric after ::