Searching on a comma separated mysql column - mysql

I am working on a project that was started with someone else. In the db instead for using a separate table the developer had opted for saving the 1 to many relationships on a single table with comma separated tables. The table structure is like this
CREATE TABLE pages(
pageid INT(6) AUTO_INCREMENT PRIMARY KEY,
newsid INT(6),
pages VARCHAR(30)
);
How can I search for a value 1 from the column pages. I have identified a few conditions that may appear, but was not been able to create a solution for it.
If I am searching for 1 the following patterns should be handles
1, match
11 shouldn't match
11, shouldn't match
,1, match
,1 match
1 match
21 shouldn't match
21, shouldn't match
I have been thinking about this for sometime, but no solution came up. I don't think normal %LIKE% can be used here
Sample sql on sqlfiddle
Also I need to search multiple values too like 1, 7 and 3

Use
FIND_IN_SET().
Example:
SELECT * FROM pages WHERE FIND_IN_SET('1', pages)
From the documentation:
FIND_IN_SET(str,strlist)
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. If the first argument is a constant string and the second is a column of type SET, the FIND_IN_SET() function is optimized to use bit arithmetic. Returns 0 if str is not in strlist or if strlist is the empty string. Returns NULL if either argument is NULL. This function does not work properly if the first argument contains a comma (“,”) character.
(highlighting added)

Related

How to update columns in a table for specific rows?

I want to update my columns for rows specified by WHERE command, but I want to update my field in a way that it extracts number part of the string from each specified field, multiplies that with a number (that I will specify) and give number output in all those specific fields extracted by WHERE command in that column.
For example, assume I want to update all my fields in a column which are like (5.6 AUD/1000, 4.5 AUD/1000, 9.7 AUD/1000), so I want to first identify fields ending with /1000 and update only those fields in the column by multiplying the number part of the string (which is 5.6, 4.5, 9.7) with any number (let's say 10). I want that other fields on the column remains unchanged.
SELECT * from sorted WHERE Column8 REGEXP '/1000$';
gives me all the specific fields that I wish to update. But I want to update them in the way I specified above, which is that I want to extract number part from the string and multiply that with a number and update those fields only.
I am able to extract all the fields with the condition I mentioned, I'm facing difficulty in update these fields in the column.
SELECT * from sorted WHERE Column8 REGEXP '/1000$';
SELECT CAST(Column8 AS UNSIGNED)*10 FROM sorted
wHERE
column8 REGEXP '/1000$';
The above code gives me required updated fields, but I want them reflected in my column.
I expect my output to be a column where only those fields ending with '/1000' should get updated in a way that the number part of the string is multiplied with 10.
I have casted the varchar field named string to decimal type and multiplied with static value 10 . I have checked in sql server.
DECLARE #temp TABLE
(
string NVARCHAR(50)
)
INSERT INTO #temp (string)
VALUES
('5.6 AUD/1000'),
('4.5 AUD/1000'),
('9.7 AUD/1000')
select cast(left(string, patindex('%[^0-9./]%', string) - 1) As decimal(18,2))* 10
from #temp

Precision loss when performing large number operation in mysql

In MySQL 5.7, a table defined as following shown
CREATE TABLE `person` (
`person_id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(64) DEFAULT NULL,
PRIMARY KEY (`person_id`),
KEY `ix_name` (`name`)
) ENGINE=InnoDB CHARSET=utf8
And then we prepared two records for testing, the value of name field (with varchar type) are
123456789123456789
1
respectively.
Case 1
select * from person where name = 123456789123456789-1;
Note that we are using a number instead of string inside the where clause. The record with name 123456789123456789 returned, and it seemed that -1 in the end are ignored!
Furthermore, we add another record with name = 123456789123456788, and this time the above select returns two records, including both 123456789123456789 and 123456789123456788;
The output looks so strange!
Case 2
select * from person where name = 123456789123456789-123456789123456788;
We could get the record with name 1, and in this case it seems that the - act as a minus operator.
Why the behavior of - in two cases are so different!
I can't immediately tell you what the type of 123456789123456789-1 is but for the comparison operation, we're almost certainly falling through most of the more "normal" data type conversion rules for mysql and ending up at:
In all other cases, the arguments are compared as floating-point (real) numbers.
Because one of the argument for the comparison (name) is a string type and the other is numeric, nothing else matches. So both get converted to floats and float types don't have too many digits of precision. Certainly less than the 18 required to represent 123456789123456789 and 123456789123456788 as two different numbers.
Look here:
SELECT person_id, name, name + 0.0, 123456789123456789-1 + 0.0, name = 123456789123456789-1
FROM person
ORDER BY person_id;
Perhaps, before comparing name = 123456789123456789-1 MySQL converts name and 123456789123456789-1 to DOUBLE as I showed in select. So some digits are lost.
Demo.

MySQL strange behavior with comma separated list of numbers

I had a very complicated problem, but i narrowed it down to this, First, let me give you some test data:
Run this:
CREATE TABLE `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`value` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
INSERT INTO test (value) VALUES
(1),
('1'),
('1,2'),
('3');
Now run this query:
SELECT * FROM test WHERE value = 1;
I would expect in this case to get only the first two rows, where the value is either entered as a numeric 1 or a '1' char, but for some reason this is what i get:
1, 1
2, 1
3, 1,2
My question is, why do i get the third row?
Note: This is my version of mysql: 5.6.28-0ubuntu0.14.04.1
Also, I already solved my original problem by using FIND_IN_SET and I am aware that it's not a very good idea to have this comma separated list type structure, ie, it should probably have been done with a join table in the first place. Unfortunately I'm working within a system that is very large and making that change is not practical at this time.
I'm just interested in why this specific behavior happens.
The reason you get the third row is implicit datatype conversion performed by MySQL. Your query has a predicate (condition) in the WHERE clause
WHERE value = 1
On the right side of the equality comparison operator (the equal sign), we have a numeric literal. On the left side, we have a column that is datatype TEXT.
It's not possible for MySQL to do a comparison of those two different datatypes.
So, MySQL converts one side or the other to a type that is compatible, so a comparison can be performed. In this case, MySQL is converting the value from the column to be numeric, so it compare to the numeric literal.
As a demonstration of what that looks like, we can add a zero (forcing MySQL to do a conversion), and exhibit the results in a SELECT.
SELECT t.value, t.value + 0 FROM test t
t.value t.value + 0
------- -----------
1 1
1 1
1,2 1
3 3
It's documented in the MySQL Reference Manual somewhere, how MySQL does the conversion. At a risk of misstating what the manual says: MySQL reads the string character by character from left to right, until it encounters a character where it can no longer convert to numeric.
In the case if the string '1,2', that happens to be the comma character. That's where MySQL stops. So the conversion returns a numeric value of 1. You would be right to point out that other databases would throw an error attempting to do a conversion of that string to numeric. But MySQL doesn't throw an error or warning.
Reference: Type Conversion in Expression Evaluation http://dev.mysql.com/doc/refman/5.7/en/type-conversion.html
Basically, the predicate in your query is equivalent to specifying:
WHERE value + 0 = 1
Which forces a conversion of the contents of the column value to numeric, and then a comparison to the numeric literal.
That's why the third row is being returned.
To get a different result, consider comparing to a string literal
WHERE value = '1'

Perform Mysql Query that masks binary string at certain locations

This question is tough but what I am looking at doing is querying binary data to check occurrences. I can't use full-text search and I'm not sure that'd help anyhow, but say I have a string in the database like 00100 (but 256 characters long) and a user tries to search the database 00101. Is there any way to find all of the rows that have a 1 in the 3rd position? Also, is there a way to do this with multiple position lookups (eg. a 1 for the 3rd and 5th position)?
I ask because I am trying to take five pieces of data and put them in one row of the database and not as five different rows. Each binary value is an boolean "occurrence" of an object, so 1 or 0.
Update:
Schema
`media_id` int(9) unsigned NOT NULL,
`256_hash` text NOT NULL,
`sequence` int(11) unsigned NOT NULL
I should have included this earlier but the actual 256 hash strings are 256 characters long. I'm assuming this it going to be a problem in the long run because its not indexable.
Sample records
media_id palette_hash sequence
1 00000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1464423415
2 00000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1464423415
You can use the REGEXP comparison.
All rows with "1" in the third position:
# The pattern reads: "anything twice, then 1, then anything"
SELECT * FROM rows WHERE (column REGEXP '^.{2}1.*$')
All rows with "1" in the third and fifth position:
# The pattern reads: "anything twice, then 1, then anything once, then 1, then anything"
SELECT * FROM rows WHERE (column REGEXP '^.{2}1.1.*$')

MYSQL find INT in string

I have a column MEDIUMTEXT that contains values that come from a goup_concat, in the form of INT,INT,INT . We can call it Concatenated_IDs
The length of the string can be of 1 int or more.
I need to break it down into original values somehow to be able to do something such as
SELECT
table_country.name
FROM
table_country
WHERE
table_country.country_id IN (
SELECT
Concatenated_IDs
FROM
table_targeted_countries
WHERE
table_targeted_countries.email LIKE "%gmail.com")
and get the country names that users registered with a gmail address target.
I have considered exploding the mediumtext into INT, creating one row for each int, sort of like a reverse concat, but I am guessing it would take a large procedure
edit:reformulated question name
You should probably normalize that table, so those concated ids are stored in a separate table, one id per record. But in the mean time, you can use mysql's find_in_set() function:
SELECT ...
WHERE FIND_IN_SET(table_country.country_id, Concatenated_IDs) > 0
Relevant docs: http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_find-in-set