I know storing lists as strings is not wise, but I have to deal with that to export some data stored that way. I also know the FIND_IN_SET function, which returns the index of a certain string in a list:
SELECT FIND_IN_SET('bar', 'foo,bar,baz');
-- selects 2
Is there any built-in function (or combination of functions) to get the string in a particular index of the list? I'm looking for something like this:
SELECT IN_SET_AT_INDEX(2, 'foo,bar,baz');
-- selects 'bar'
I'd like to avoid a split-like function that makes the list a separate table, if possible.
SUBSTRING_INDEX() can do this, sort of:
mysql> SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('foo,bar,baz', ',', 2), ',', -1) AS middle_one;
+------------+
| middle_one |
+------------+
| bar |
+------------+
Yes, but it requires just a little trickery. The basis is substring_index(), but that gets everything up to the nth entry. Then I use reverse() twice and another substring_index():
select reverse(substring_index(reverse(substring_index('foo,bar,baz', ',', 2)), ',', 1))
In your case, the transformations are:
original string: 'foo,bar,baz'
after substring_index(..., 2) 'foo,bar'
after inner reverse 'rab,oof'
after substring_index(..., 1) 'rab'
after outer reverse 'bar'
Related
I have a table with a varchar column that represents a path. I want to search for rows that have a path that follow a pattern like name.name[*] where name can be anything. I am looking for repeated strings contained anywhere in the path column that are separated by a period and have a square bracket after them.
This seems to call for Regexp, so through python I have something like https://regex101.com/r/apS20a/4
However, trying to implement this with MySQL Regexp is not working. I have been able to translate the shorthand into REGEXP '([A-Za-z_]+).(\1[[0-9]+])', but it seems that MySql Regex does not support capture groups. Is there a way to accomplish what I am trying to do with mysql regexp? Thank you
I don't think that MySQL supports capture groups. But if you only have one example of .name[ in the string between the first . and the first [, you can hack your way around it. This is not a general solution, just a specific approach in this case.
You can get the name with:
select substring_index(substring_index(url, '[', 1), '.', -1) as name
And then incorporate this into a regular expression:
select t.*
from (select t.*,
substring_index(substring_index(url, '[', 1), '.', -1) as name
from t
) t
where url like concat('%', name, '.', name, '[%');
This just uses like instead of regexp, because [ and . are regular expression wildcards. Of course, this assumes that name does not have _ or %.
EDIT:
Here is a method that actually identifies when this occurs -- and works even if there are multiple patterns.
The idea is to construct the regular expression based on what happens between the . and [ -- and then to apply it. Delightfully self-referential:
select t.*,
(url regexp regex)
from (select t.*,
substr(regexp_replace(url, '[^.]*[.]([^\\[]*)\\[[^.]*', '|$1[.]$1\\\\['), 2) as regex
from (select 'abcde.de[12345.345[ABC' as url union all
select 'abcdefdef[[[[..123.124['
) t
) t;
Here is the above in a db<>fiddle.
I am looking to compare the results of 2 cells in the same row. the way the data is structured is essentially this:
Col_A: table,row,cell
Col_B: row
What I want to do is compare when Col_A 'row' is the same as Col_B 'row'
SELECT COUNT(*) FROM MyTable WHERE Col_A CONTAINS Col_B;
sample data:
Col_A: a=befd-47a8021a6522,b=7750195008,c=prof
Col_B: b=7750195008
Col_A: a=bokl-e5ac10085202,b=4478542348,c=pedf
Col_B: b=7750195008
I am looking to return the number of times the comparison between Col_A 'b' and Col_B 'b' is true.
This does what I was looking for:
SELECT COUNT(*) FROM MyTable WHERE Col_A LIKE CONCAT('%',Col_B,'%');
I see You answered Your own question.
SELECT COUNT(*) FROM MyTable WHERE Col_A LIKE CONCAT('%',Col_B,'%');
is good from performance perspective. While normalization is very good idea, it would not improve speed much in this particular case. We must simply scan all strings from table. Question is, if the query is always correct. It accepts for example
Col_A: a=befd-47a8021a6522,ab=7750195008,c=prof
Col_B: b=7750195008
or
Col_A: a=befd-47a8021a6522,b=775019500877777777,c=prof
Col_B: b=7750195008
this may be a problem depending on the data format. Solution is quite simple
SELECT COUNT(*) FROM MyTable WHERE CONCAT(',',Col_A,',') LIKE CONCAT('%,',Col_B,',%');
But this is not the end. String in LIKE is interpreted and if You can have things like % in You data You have a problem. This should work on mysql:
SELECT COUNT(*) FROM MyTable WHERE LOCATE(CONCAT(',',Col_B,','), CONCAT(',',Col_A,','))>0;
SELECT * FROM MyTable WHERE Col_A = Col_B (AND Col_A = 'cell')
^^ Maybe you are looking for this statement. The part in brackets is optional.
If this is not the solution, please supply us with further information.
The easiest way would be to use the IN operator.
SELECT COUNT(*) FROM MyTable WHERE Col_A IN (Col_B);
More info on the IN operator: http://www.w3schools.com/sql/sql_in.asp
There's also the SUBSTRING() or MID() (depending on what you're using) function if you know that the substring will be in the same position everytime.
MID()/SUBSTRING() function: http://www.w3schools.com/sql/sql_func_mid.asp
You can use SUBSTRING_INDEX to extract a delimited field from a column.
SELECT COUNT(*)
FROM MyTable
WHERE Col_B = SUBSTRING_INDEX(SUBSTRING_INDEX(Col_A, ',', 2), ',', -1)
You need to call it twice to get a single field. The inner call gets the first two fields, the outer call gets the last field of that.
Note that this will be very slow if the table is large, because it's not possible to index substrings in MySQL. It would be much better if you normalized your schema so each field is in a separate column.
If column Col_a has data with format table,row,cell then search expression will be next:
SELECT COUNT(*) FROM MyTable AS MT
WHERE SUBSTRING(Col_A,
INSTR(Col_A, ',b=') + 3,
INSTR(Col_A, ',c=') - INSTR(Col_A, ',b=') + 3) = Col_B
I have data stored in a comma-separated format and I would like to run a query so that the users are ordered by the second value in the column.
So for this sample data:
user | Data
________________________________________
player1 | 45471,2529,32196008193896,99
admin | 1136,2595,17760808279311,95
gamer | 13495,2432,32196008193896,98
________________________________________
The order would be (2595-2529-2432) which is admin => player1 => gamer.
As I mentioned in the comments, you should really try to avoid storing delimited lists like that.
However, you can write a query to parse out the second value by using the SUBSTRING_INDEX() function. This function takes a string, the delimiter character, and an integer whether or not to take from the left/right of the delimiter and for how many. In other words, to get everything left of the second comma, it would look like this:
SELECT SUBSTRING_INDEX(data, ',', 2)
FROM myTable;
Then, from that string, you want everything to the right of the first comma, so you'll have to nest the functions. (Note, this may hurt efficiency, but that's the downfall of delimited lists):
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(data, ',', 2), ',', -1)
FROM myTable;
Then, you can just order by that value:
SELECT *
FROM myTable
ORDER BY SUBSTRING_INDEX(SUBSTRING_INDEX(data, ',', 2), ',', -1) DESC;
Here is an SQL Fiddle example.
I have a table with a column that has CSV.
TableA:
field_id | matches
---------------------
1 1,2,4,6,8,11,14,56
Now I need to get the field_id that matches a user given csv. So for instance, user string is 1,4,11, then it should return some value may be just true.
1.) Find_in_set does not work. Because it takes only one element and searches that in a SET/CSV column.
2.) Cannot use like concat('%,', user_input , ',%'). Because user input may not be in order.
Any other ideas? I guess this is a very common scenario.
Note: I dont need to search all records. I need to search a specific record. So in the above table, I just need to search one record that has field_id = 1. i.e. (where field_id = 1). (May not matter, but just an info)
Well, this is a good argument for having data in a proper relational form. But, you can try:
select t.*
from t
where (find_in_set($user_input, 1) = 0 or
find_in_set(substring_index(substring_index($user_input, ',', find_in_set($user_input, 1)), ',', -1), matches) > 0) and
(find_in_set($user_input, 2) = 0 or
find_in_set(substring_index(substring_index($user_input, ',', find_in_set($user_input, 2)), ',', -1), matches) > 0) and
. . .
Do this for however many values you might have in the userinput set.
I presume there is no straight solution with MySQL query like Find_In_Set. So I guess I will have to handle this with multiple queries or with Looping.
My table column has rows like this 1001, 1002, 1003 and so on separated by commas. There may be 1 or more comma separated values in each column. I need the total count of these comma separated values in the table. For example if my table has 2 rows one having 1001, 1002, 1003, 1004 and another with 1001, 1005 the output i should get is 6 i.e. 4+2. Kindly assist
there is no function in mysql to count char occurences. but you can replace every comma with nothing. and then you calculate the difference of lenghts which will give you the number of commas, which is one less than the number of values.
select
( LENGTH(col1) - LENGTH(REPLACE(col1, ',', '')) + 1 )
+ ( LENGTH(col2) - LENGTH(REPLACE(col2, ',', '')) + 1 )
AS valCount
from T;
(didn't test that explicitely but at least something very similar to that will do the job.)
replace()
length()
Try:
SELECT SUM(LEN(ColumnName) - LEN(REPLACE(ColumnName, ',', ''))) FROM TableName
This is one of those tasks that'd be much, much easier in the server-side script accessing your database than the database itself. Assuming you've already assigned the comma-separated strings to an array (where $array[1] is equal to the string from row 1:
$array = array("1001, 1002, 1003, 1004", "1001, 1005"); // assigned from database
foreach($array as $k => $v){
$numbersInString[$k] = count(explode(', ', $v));
}
echo implode(' + ',$numbersInString);
This is possible, with creative solutions (such as that from Raffael1984), in MySQL, but seems to much more easily, and concisely, implemented in PHP.
References:
count().
explode().
implode().
foreach().
Did you try using the count() function? You can specify which rows if you need to.
.row[COUNT(name)]
in your query. What does your table look like? I might be able to help more if I know what it looks like