Multiple LIKE in SQL - mysql

I wanted to search through multiple rows and obtain the row that contains a particular item.
The table in mySQL is setup so each id has a unique list (comma-delimited) of values per row.
Ex:
id | order
1 | 1,3,8,19,34,2,38
2 | 4,7,2,190,38
Now if I wanted to pull the row that contained just the number 19 how would I go about doing this? The possibilities I could figure in the list with a LIKE condition would be:
19, <-- 19 at the start of the list
,19 <-- 19 at the end of the list
,19, <-- 19 inside the list
I tried the following and I cannot obtain any results, Thank you for your help!
SELECT *
FROM categories
WHERE order LIKE '19,%' OR '%,19%' OR '%,19%'
LIMIT 0 , 30

First of all, you should not store values like this, you should use a child table with one row per item, this would make your current query that much easier to handle, and would allow for indexes to be used as well.
Having said that, what you want is this:
WHERE ',' + order + ',' LIKE '%,19,%'
By adding a comma in front of and after the list of values in the order field, you can query that field for the value you want with a comma on each side, and not have to deal with the special case of the value being the first or last value in the list.
With the query you listed, you could've made it work by realizing that OR doesn't give more arguments to the LIKE clause, it separates entirely different clauses, and thus rewritten the SQL like this:
WHERE order LIKE '19,%' OR order LIKE '%,19,%' OR order LIKE '%,19'
---+------ ^ ----+----- ^
| | | |
+- add this -+---------+ +- removed
percent
Note the missing comma in the second pattern there, which would match strings containing the value 19, and note the removed percentage character in the last ,which would allow it to match lists that ended with 19.

You can use FIND_IN_SET to solve your problem in a simpler way:
SELECT *
FROM categories
WHERE FIND_IN_SET('19', `order`)
LIMIT 0, 30
In general though you should try to design your database so that this type of operation is not necessary.

Related

MySQL - Convert single column into an array

I have a column of data, e.g. as follows:
select league_id from leagues
This gives me a single column (league_id) and 100+ rows for that column.
I want to convert it into a single cell (1 row, 1 column) with the following structure:
[1001, 1002, 42022, 203412, 24252, etc..]
Essentially converting the rows into one big array.
There must be a way of doing it but can't see how.
I'm using MariaDB 10.2.
You can use the GROUP_CONCAT() function for that.
Usage is straightforward:
id
val
1
1001
2
1002
3
42022
4
203412
5
24252
SELECT group_concat(val)
FROM tab
gives you
group_concat(val)
1001,1002,42022,203412,24252
See db<>fiddle.
(Note: Before MariaDB 10.3.3 you cannot use the LIMIT clause with GROUP_CONCAT, in case you should need that).

Update a row if a field is a subsequence of a string

I have a string S = "1-2-3-4-5-6-7-8"
This is how my database table rows look like:
id
SubSequence
1
1-2-4-5
2
1-3-4-5
3
2-5-7-8
4
5-8-9-10
5
6-7-10-11
and so on ...
I want to write a query that would update (in this example) only the first 3 rows because they're a subsequence of string S.
The current solution I have is to programmatically go thru each row, check if it's a subsequence, and update. But I'm wondering if there's a way to do it at the MySQL level for performance.
Update: I don't mind changing the way data is stored. For example, String S could be an array holding those numbers, and the "SubSequence" column can hold those numbers as an array.
No, there is not a way to do the query you describe with good performance in SQL when you store the subsequences as strings like you have done. The reason is that doing substring comparisons cannot be optimized with indexes, so your query will be forced to do the comparisons row by row.
In general, when you try to store sets of values as a string, but you want to use SQL to treat them as discrete values, it's bound to be awkward, difficult to code, and ultimately have bad performance.
In this case, what I would do is make a two tables, one that numbers your entities, and a second table in which each value in your subsequence is stored on a row by itself.
SubSequences:
id
1
2
SubSequenceElements:
id
SubSequenceElement
1
1
1
2
1
4
1
5
2
1
2
3
2
4
2
5
And so on.
Then you can use relational-division techniques to find cases where every element of this set exists in the set you want to compare it to.
Here's an example:
SELECT s.id
FROM SubSequences AS s
LEFT OUTER JOIN (
SELECT id
FROM SubSequenceElements
WHERE SubSequenceElement NOT IN (1,2,3,4,5,6,7,8)
) AS invalid USING (id)
WHERE invalid.id IS NULL;
In other words, you want to return rows from SubSequences such that no match is found in SubSequenceElements with an element value that is not in the set you're trying to match.
It's a bit confusing, because you have to think about the problem is a double-don't-match-this-set problem. But once you get relational division, it can be very powerful.
If the set can be represented by the numbers 0 through 63 (or some subset of that), then...
Using a column like this
elements BIGINT UNSIGNED NOT NULL DEFAULT '0'
Then "2-5-7-8" could be put into it thus:
UPDATE ...
SET elements = (1<<2) | (1<<5) | (1<<7) | (1<<8);
Then various operations can be done in a single expression:
WHERE elements = (1<<2) | (1<<5) | (1<<7) | (1<<8) -- Test for exactly that set
WHERE (elements ^ ~ ( (1<<2) | (1<<5) | (1<<7) | (1<<8) )) != 0
-- checks to see if any other bits are turned on
This last example is close to what you need. One side of the "and not" would have the 1..8 of your example, the other would have
Your example has S represented as 0x1FE;
WHERE subsequence & ~0x1FE
will be 0 (false) for ids 1,2,3; non-zero (true) for ids 4 and 5.

MySql SELECT by Spliting multiple values separated by ||

Below is the result set from SELECT query,
mysql> select * from mytable where userid =242 ;
+--------+-----------------------------+------------+---------------------+---------------------+
| UserId | ActiveLinks | ModifiedBy | DateCreated | DateModified |
+--------+-----------------------------+------------+---------------------+---------------------+
| 242 | 1|2|4|6|9|15|22|33|43|57|58 | 66 | 2013-11-28 16:17:25 | 2013-11-28 16:17:25 |
+--------+-----------------------------+------------+---------------------+---------------------+
What I want is to SELECT the records by splitting the Active links columns and associating it with UserId in the below format,
eg,
UserId ActiveLinks
242 1
242 2
242 4
242 6
Can anyone help me with this query , as of now nothing coming to my mind. Thanks
Dealing with lists stored in data is a pain. In MySQL, you can use substring_index(). The following should do what you want:
SELECT userid,
substring_index(substring_index(l.ActiveLinks, '||', n.n), '|', -1) as link
FROM (select 1 as n union all select 2 union all select 3 union all select 4) n join
ipadminuserslinks l
on length(l.ActiveLinks) - length(replace(l.ActiveLinks, '||', '')) + 1 <= n.n
WHERE userid = 242;
The first subquery generates a bunch of numbers, which you need. You may have to increase the size of this list.
The on clause limits the numbers to the number of elements in the list.
As you can probably tell, this is rather complicated. It is much easier to use a junction table, which is the relational way to store this type of information.
I would create a routine which will have the delimiter as an argument.
Another in_var would be the correspondent line.
Every time you call it, it will return the set of values for the UserId called.
It will basically use a loop based on the count of '|' (we call this pipeline)
This way you can implement the solution proposed by #Gordon Linoff without the need to know how many active links you have.
If this is just a list of values that do not relate to anything on another table I would do it the same way as Gordon (if needs be you can cross join the sub query that gets the lists of numbers to easily generate far larger ranges of numbers). One minor issue is that if the range of number is bigger than the number of delimited values on a row then the last value will be repeated (easily removed using DISTINCT in this case, more complicated when there are duplicate values in there that you want to keep).
However if the list of delimited values are related to another table (such as being the id field of another table then you could do it this way:-
SELECT a.UserId, b.link_id
FROM mytable a
LEFT OUTER JOIN my_link_table b
ON FIND_IN_SET(b.link_id, replace(a.ActiveLinks, '|', ','))
Ie, use FIND_IN_SET to join your table with the related table. In this case converting any | symbols used as delimiters to commas to allow FIND_IN_SET to work.

How to explode in MySQL and use it in the WHERE clause of the query - MySQL

I have a database table as below.
Promotion_Table
id(INT), promotion_name(VARCHAR),......, bungalow_ids(VARCHAR)
We can add a promotion for a bungalow(23). So a row is added with the bungalow id as below.
1, My Promotion, ........, 23
But if I single a promotion is added for a multiple bungalows(23,42) all ids are saved in the bungalow_ids column as below.
2, My Promotion 2, ........, 23 | 42
If a user search for promotion which are for specific bungalow(23) All promotions for the bungalow should be shown in the result.
I have a query as below.
SELECT * FROM Promotion_Table WHERE bungalow_ids = '23'
It only gets 1 rows. But actually 2nd row should be shown too since there is a offer. I can nt use LIKE since it gets wrong records.
Given that I have already referred below links but I have no idea how to use them in the query.
Can you split/explode a field in a MySQL query?
Equivalent of explode() to work with strings in MySQL
How can I fix this? How can I explode the column data and use it in the query ?
Use , to separate the string and try this query
select * from promotion_table where FIND_IN_SET("23",bungalow_ids)
http://sqlfiddle.com/#!2/7bbcb/1
The previous Answer is the right decision but if you insist in your model.
Probably what you want to do is:
SELECT *
FROM Promotion_Table
WHERE bungalow_ids = '23'
OR bungalow_ids LIKE '23,*'
OR bungalow_ids LIKE '*,23'
OR bungalow_ids LIKE '*,23,*'
this assuming the numbers are separated by ",".
But this is the wrong way, make the changes to the DB as stated in the previous answer.
you need to reformat your DB schema.
you need to construct 2 tables one for promotions and one for bangalows.
like below:
promotions: Promotion_id(int), Promotion_desc
bangalows: Bangalow_id(int), Promotion_id(int)
tables example:
promotion :
1 myPromotion
2 secondPromotion
bangalows:
1 1
2 2
3 1
4 1
once you create above two tables, the following query will work and returns 1,3,4:
SELECT Bangalow_id FROM Promotion_Table WHERE bungalow_id = '1'

Is there a possibility to change the order of a string with numeric value

I have some strings in my database. Some of them have numeric values (but in string format of course). I am displaying those values ordered ascending.
So we know, for string values, 10 is greater than 2 for example, which is normal. I am asking if there is any solution to display 10 after 2, without changing the code or the database structure, only the data.
If for example I have to display values from 1 to 10, I will have:
1
10
2
3
4
5
6
7
8
9
What I would like to have is
1
2
3
4
5
6
7
8
9
10
Is there a possibility to ad an "invisible character or string which will be interpreted as greater than 9". If i put a10 instead of 10, the a10 will be at the end but is there any invisible or less visible character for that.
So, I repeat, I am not looking for a programming or database structure solution, but for a simple workaround.
You could try to cast the value as an number to then order by it:
select col
from yourtable
order by cast(col AS UNSIGNED)
See SQL Fiddle with demo
You could try appending the correct number of zeroes to the front of the data:
01
02
03
..
10
11
..
99
Since you have a mixture of numbers and letters in this column - even if not in a single row - what you're really trying to do is a Natural Sort. This is not something MySQL can do natively. There are some work arounds, however. The best I've come across are:
Sort by length then value.
SELECT
mixedColumn
FROM
tableName
ORDER BY
LENGTH(mixedColumn), mixedColumn;
For more examples see: http://www.copterlabs.com/blog/natural-sorting-in-mysql/
Use a secondary column to use as a sort key that would contain some sort of normalized data (i.e. only numbers or only letters).
CREATE TABLE tableName (mixedColumn varchar, sortColumn int);
INSERT INTO tableName VALUES ('1',1), ('2',2), ('10',3),
('a',4),('a1',5),('a2',6),('b1',7);
SELECT
mixedColumn
FROM
tableName
ORDER BY
sortColumn;
This could get difficult to maintain unless you can figure out a good way to handle the ordering.
Of course if you were able to go outside of the database you'd be able to use natural sort functions from various programming languages.