MySQL Query conditional find nth element in column string - mysql

I have a MySQL table setup where one column's values are a string of comma-separated True/False values (1s or 0s). For example, in the column, one field's value may be "0,1,0,0,0,0,1,1,0" and another may be "1,0,0,1,1,1,0,0,0" (note: these are NOT 9 separate columns, but a string in one column). I need to QUERY the MySQL table for elements that are "true"(1) for the "nth element" of that column's value/string.
So, if I was looking for rows, with a specific column, where the 3rd element of the column's value was 1, it would produce a list of results. So, in this case, I would only be searching for "1" in the fth place (12345 = X,X,X...) of the string (X,X,1,X,X,X,X,X,X,X). How can I query this?
This is a crude example of what I am trying to do ...
"SELECT tfcolumn FROM mytable WHERE substr({tfcolumn}, 0, 5)=1"
{tfcolumn} represents the column value
5 represents the 5th position of the string
=1 represents what I need that position to equal to.
Please help. Thanks

You can't. Once you put a serialized data type into a column in SQL (like comma separated lists, or JSON objects) you are preventing yourself from performing any query on the data in those columns. You have to pull the data in a different way and then use a program like python, VB, etc to get the comma separated values you are looking for.
Unless you want to deal with trying to make this mess of a query work...
I would recommend changing your table structure before it's too late. Although it is possible, it is not optimized in a format that a DBMS recognizes. Because of that the DBMS will spend a significant amount of time going through every record to parse the csv values which is something that it was not meant to be doing. Doing the query in SQL will take as much time (if not more time) than just pulling all the records and searching with a tool that can do it properly.

If the column contains values exactly like the ones you posted, then the Nth element is at the 2 * N - 1 position in the comma separated list.
So do this:
SELECT tfcolumn
FROM tablename
WHERE substr(tfcolumn, 2 * 5 - 1, 1) = '1'
Replace 5 with the index that you search for.
See the demo.
Or remove all commas and get the Nth char:
SELECT tfcolumn
FROM tablename
WHERE substr(replace(tfcolumn, ',', ''), 5, 1) = '1'
See the demo.

Try this
if substring_index(substring_index('0,1,0,0,0,0,1,1,0',',',3),',',-1)='1'
The first argument can be your column name. The second argument (',') tells the function that the string is comma-separated. The third argument takes the first 3 elements of the string. So, the output of inner substring_index is '0,1,0'.
The outer substring_index has -1 as the last argment. So, it starts counting in reverse direction & takes only 1 element starting from right.
For example, if the value in a particular row is '2,682,7003,14,185', then the value of substring_index(substring_index('2,682,7003,14,185',',',3),',',-1) is '7003'.

Related

How do I create a SELECT conditional in MySQL where the conditional is the character length of the LIKE match?

I am working on a search function, where the matches are weighted based on certain conditions. One of the conditions I want to add weight to is matches where the character length of the query string in a LIKE match is longer than 4.
This is what I want to the query to look like, roughly. %s is meant to represent the actual match found by LIKE, but I don't think it does. I'm wondering if there is a special variable in MySQL that does represent the precise character match found by LIKE.
SELECT help.*,
IF(CHAR_LENGTH(%s) > 4, 2, 0) w
FROM help
WHERE (
(title LIKE '%this%' OR title LIKE '%testy%' OR title LIKE '%test%') OR
(content LIKE '%this%' OR content LIKE '%testy%' OR content LIKE '%test%')
) LIMIT 1000
edit: I could in the PHP split the search string array into two arrays based on the character length of the elements, with two separate queries that return different values for 'w', then combine the results, but I'd rather not do that, as it seems to me that would be awkward, messy, and slow.
Check out FULLTEXT as another way to discover rows. It will be faster, but won't address your question.
This probably has the effect you want.
SELECT ....
IF ( (title LIKE '%testy%' OR
content LIKE '%testy%'), 2, 0)
....
Note that the "match" in your LIKEs includes the %, so it is the entire length of the string. I don't think that is what you wanted.
REGEXP "(this|testy|that)" will match either 4 or 5 characters (in this example). It may be possible to do something with REGEXP_REPLACE to replace that with the empty string, then see how much it shrank.
I think the answer to my question is that what I wanted to do isn't possible. There is no special variable in MySQL representing the core character match in a WHERE condtional where LIKE is the operator. The match is the contents of the returned data row.
What I did to reach my objective was took the original dynamic list of search tokens, iterated through that list, and performed a search on each token, with the SQL tailored to the conditions that matched each token.
As I did this I built an array of the search results, using the id for the database row as the index for the array. This allowed me to perform calculations with the array elements, while avoiding duplicates.
I'm not posting the PHP code because the original question was about the SQL.

MySQL Select Row Where Column Contains a Value

I have tried using 'LIKE' but it runs into problems which i will explain below.
i have a string column that looks like any of these.
"1010, 2020, 3030"
"1010"
""
I want to be able to see if this string contains a single ID. e.g 2020. if it does then return the row. I tried using like but if the id is 20 it will return the row because 2020 contains 20.
Selecting the entire db and then using a delimiter to go through all the strings will take far too much time. Is it possible to implement this?
This is why you don't store multiple values in a single field. Because your bad design, this is the query structure you'll have to use EVERY SINGLE TIME to compensate for it:
WHERE
foo = 2020 // exact match, only value in field
OR foo LIKE '2020,%' // value is at start of field
OR foo LIKE '%,2020,%' // value is somewhere in the middle of the field
OR foo LIKE '%,2020' // value is at the end of the field
Or you could have had a properly normalized design, and just done
WHERE childtable.foo = 2020
and be done with it.
First, you should not store lists of things in string variables. SQL has a very nice data structure for lists. It is called a table. Each row in such a table would have an id and one value from the list.
That said, sometimes you are stuck with data like this. In that case, you can use find_in-set():
where find_in_set('20', replace(stringcolumn, ', ', ',')) > 0;
You can also do the logic with like, but MySQL has the convenient built-in function for this.
EDIT:
If you want to do this with like:
where concat(',', stringcolumn, ',') like '%,20,%'
Note that the delimiters "protect" the values, so 20 is not confused with 2020.

Finding number of occurence of a specific string in MYSQL

Consider the string "55,33,255,66,55"
I am finding ways to count number of occurence of a specific characters ("55" in this case) in this string using mysql select query.
Currently i am using the below logic to count
select CAST((LENGTH("55,33,255,66,55") - LENGTH(REPLACE("55,33,255,66,55", "55", ""))) / LENGTH("55") AS UNSIGNED)
But the issue with this one is, it counts all occurence of 55 and the result is = 3,
but the desired output is = 2.
Is there any way i can make this work correct? please suggest.
NOTE : "55" is the input we are giving and consider the value "55,33,255,66,55" is from a database field.
Regards,
Balan
You want to match on ',55,', but there's the first and last position to worry about. You can use the trick of adding commas to the frot and back of the input to get around that:
select LENGTH('55,33,255,66,55') + 2 -
LENGTH(REPLACE(CONCAT(',', '55,33,255,66,55', ','), ',55,', 'xxx'))
Returns 2
I've used CONCAT to pre- and post-pend the commas (rather than adding a literal into the text) because I assume you'll be using this on a column not a literal.
Note also these improvements:
Removal of the cast - it is already numeric
By replacing with a string one less in length (ie ',55,' length 4 to 'xxx' length 3), the result doesn't need to be divided - it's already the correct result
2 is added to the length because of the two commas added front and back (no need to use CONCAT to calculate the pre-replace length)
Try this:
select CAST((LENGTH("55,33,255,66,55") + 2 - LENGTH(REPLACE(concat(",","55,33,255,66,55",","), ",55,", ",,"))) / LENGTH("55") AS UNSIGNED)
I would do an sub select in this sub select I would replace every 255 with some other unique signs and them count the new signs and the standing 55's.
If(row = '255') then '1337'
for example.

Assigning Each Like Statement A Value That Is Returned On Matching

I have a string array comprised of words (ex. { alpha, beta, gamma } ) and a MySQL table filled with words. For each string array, I come up with a SELECT statement that queries the MySQL table to see if there are any matches of words. The rows that are returned let me know when a word in my string array is one of the unique ones in the table. I then alter the text of the specific string in the string array. For simplicity sake, let's assume I want to call a .ToUpper() on it.
My current method is to get all of the rows from the table that match and then loop through the string array and check whether every single row returned matches every single string in the array. That's incredibly inefficient, and I would much rather have the rows returned from my MySQL query have a column that tells me which position in the string array that word came from. That way I can jump right there and fiddle around with the string. Is there a way to give each "LIKE" clause in the where statement a unique identifier that is returned if that specific like clause is the one that matches? Any ideas would be much appreciated.
My Select Statement is:
SELECT `WordText` FROM `Words` WHERE `WordText` LIKE 'alpha%' OR `WordText` LIKE 'gamma%';
What I am looking for is something like this:
SELECT `WordText`, PositionInArray FROM `Words` WHERE `WordText` LIKE 'alpha%' (IF MATCH THEN PositionInArray=0 -- alpha's position in my array) OR `WordText` LIKE 'gamma%' (IF MATCH THEN PositionInArray=2 - gamma's position in my array);
When this row is returned, I can go straight to WordArray[PositionInArray].ToUpper() and know that that is the word that matched.
Thanks a lot!
SELECT CASE WHEN WordText LIKE 'alpha%' THEN 0 WHEN WordText LIKE 'beta%' THEN 1
WHEN 'gamma%' THEN 2 ELSE -1 END AS match FROM Words WHERE match <> -1

Is there any way to modify a column before it is ordered in MySQL?

I have a table with a field value which is a varchar(255). The contents of the field can be quite varied:
$1.20
$2994
$56 + tax (This one can be ignored or truncated to $56 if necessary)
I have a query constructed:
SELECT value FROM unnamed_table ORDER BY value
However, this of course uses ASCII string comparison to order the results and does not use any numerical type of comparison.
Is there a way to truly order by value without changing the field type to DECIMAL or something else? In other words, can the value field be modified ('$' removed, value converted to decimal) on the fly before the results are sorted?
You could sort on an expression made to "parse" the text into decimal
SELECT value FROM unnamed_table ORDER BY expression_returning_decimal(value)
Where your expression uses MySQL functions to extract the number from the string depending on what form you expect it to take (something like CAST(value AS DECIMAL(10,2)), but you'll probably need to deal with the extraneous non-numeric characters somehow as I'm not sure what the CAST will give if you don't strip them).
Create a second column without the $-sign, sort on that one and use the data of the original column in your application.
In order to create the helper column and sort on it you would need something like this:
SELECT value, CAST(SUBSTR(value, 2) AS UNSIGNED) sort_col
FROM unnamed_table ORDER BY sort_col