How to optimize search in list in SQL - mysql

I have to make a SQL query in Mysql to search a string list (for ex: 1,2,3) in a columns (for ex: list_id), which also have string value list (1,2,3).
For more detail, my_table is
+-----------+----------+
| id | list_id |
+-----------+----------+
| 1 | 29 |
| 2 | 30 |
| 3 | 31 |
| 4 | 4,5,6,7 |
| 5 | 8,9,10,11|
| 6 | 4,5,8,9 |
| 7 | 1,2,3,6 |
+-----------+----------+
The search value is 1,5,8 and I need get the rows have list_id have 1 or 5 or 8 in it's list. Therefore, the result wil be:
+-----------+----------+
| id | list_id |
+-----------+----------+
| 4 | 4,5,6,7 |
| 5 | 8,9,10,11|
| 6 | 4,5,8,9 |
| 7 | 1,2,3,6 |
+-----------+----------+
My query string is:
SELECT * FROM my_table
WHERE list_id LIKE '%,1,%'
OR list_id LIKE '1,%'
OR list_id LIKE '%,1'
OR list_id LIKE '%,5,%'
OR list_id LIKE '5,%'
OR list_id LIKE '%,5'
OR list_id LIKE '%,8,%'
OR list_id LIKE '8,%'
OR list_id LIKE '%,8'
It is match correct what I want. However, the length of query is in proportion to length of list.
Does REGEXP is better than LIKE in this circumstance?
Does anyone have experience to make another solution better?

You may try to concatenate commas to your field (or use SET in MySQL or make a better database structure - in which you join on tables in which the related data is stored).
SELECT * FROM yourtable WHERE CONCAT(',', fieldname, ',') like '%,1,%';

Yes, regular expressions will work for this. Here is what you can do:
SELECT * FROM junk
WHERE CONCAT(',', list_id, ',') REGEXP CONCAT(',(', REPLACE('1,3,8',',','|'), '),');
Results:
ID | LIST_ID
5 | 8,9,10,11
6 | 4,5,8,9
7 | 1,2,3,6
Please see SQL Fiddle demo here.
We turn the query list 1,3,8 into an alternating group 1|3|8. You might be able to do this in your application code to avoid using the REPLACE() function above.
UPDATE Apologies, I mistakenly used 1,3,8 as the query parameter instead of 1,5,8. But it should still work.

I am going to strongly suggest that you change the design of the database (I am assuming you have some control or influence over it).
You should make the id column non-unique and then the list_id column should contain a single value. You can then search as follows:
SELECT id WHERE list_id IN (1,5,8)
If it is a big table and there are a lot of list_id values, put an index on the list_id column.
If you need the output in a comma-separated list, then you will need to use an aggregating concatenation function with GROUP BY (e.g., GROUP_CONCAT() in MySQL).
If you cannot change the design of the schema, then use one of the other suggestions here.

Related

MySQL Query results

Table name : Students.
The Table i have:
mysql> SELECT * from Students;
+-----------+-------------+-------+
| Rollno | Name | Marks |
+-----------+-------------+-------+
| 251602122 | Sumit Tyagi | 70 |
| 251602121 | parveen | 90 |
+-----------+-------------+-------+
Following query returns the following result even 8 is not a attribute.
mysql> select 8 from Students;
+---+
| 8 |
+---+
| 8 |
| 8 |
+---+
Similarly
mysql> SELECT 'some_string' from Students;
+-------------+
| some_string |
+-------------+
| some_string |
| some_string |
I just want to know why this happens.
The query returns one line for every record in your table.
But you don't select data from those record. You just select the number 8 for each line. And this gets returned.
Select statement looks for column name in a table. You can make sure SQL look for a column name in a table by using TableName.ColumnName.
In the example you wrote, you are asking for a constant or hardcoded value 8/some_string to be returned from the table which is not the column name. So it will return the hardcoded or constant value you asked for, the number of times equal to number of rows in your table.
If you want to make sure it look for the column name, use the syntax I mentioned as TableName.ColumnName. You can also provide an alias for your table. So in the example above, if you use the syntax as
SELECT Students.8 from Students;
or
SELECT s.8 FROM Students s;
It will look for column name as 8 instead of constant or hardcoded value 8.
If I am not wrong, it is a best practice to use TableName.ColumnName or alias.ColumnName while writing queries as it checks for column name in that particular table.

How to get titles(a row in a table) from MySql database so that the titles are in a particular order

I have the following query
SELECT id,title,image,address,serviceType FROM `ta` ORDER BY title LIMIT 0,20
It gives me results based on ordering of the titles.
The default ordering puts 'titles' starting with special characters first, then titles starting with numbers and then titles starting with alphabets.
But I want, first titles starting with numbers, then titles starting with alphabets and then titles starting with special characters.
Please help.
You can put case statement in your order by clause. Somthing like this:-
SELECT id, title, image, address, serviceType
FROM `ta`
ORDER BY CASE WHEN title LIKE '[0-9]%' THEN 1
WHEN title LIKE '[A-Z]%' THEN 2
ELSE 3 END
LIMIT 0,20
Hope this helps.
How to ask a question:
Hello, I have a table like this:
CREATE TABLE strings
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,string VARCHAR(12) UNIQUE NOT NULL
);
INSERT INTO strings (string) VALUES
('apple'),
('banana'),
('cherry'),
('*durian'),
('1eggplant'),
('10fig'),
('grapefruit');
SELECT * FROM strings;
+----+------------+
| id | string |
+----+------------+
| 4 | *durian |
| 6 | 10fig |
| 5 | 1eggplant |
| 1 | apple |
| 2 | banana |
| 3 | cherry |
| 7 | grapefruit |
+----+------------+
I would like a result set like this...
[PUT YOUR DESIRED OUTPUT HERE]
... where the rows are arranged by x, y z.
I tried this...
[PUT YOUR BEST EFFORT HERE]
... but that's not right because it's wrong in the following ways...

Compare a value from a CSV column in mysql with more checks

I need to have a query which has two parts.
I mean two where clause.
till now I have reached to the following, but it dosen't work properly.
SELECT * from items
where
FIND_IN_SET( '3', category_id )
AND postcode LIKE 'sw19%'
order by id
this query only runs the first part, i.e., the FIND_IN_SET() part. and just ignores the second part i.e., Postcode check...
I want to know is there a solution for this kind of query.
DETAILS
I want to compare the values from 2 columns:
1 column is a csv column , eg. 1,2,3,4,5
the other is postcode B17 SW19 etc
Table is like the following.
---+------+-------------+----------+-
id | item | category_id | postcode |
---+------+-------------+----------+-
1 | abc | 1,2,3,4 | SW19 |
---+------+-------------+----------+-
2 | def | 3,4,5 | NW6 |
---+------+-------------+----------+-
3 | xyz | 6,7,8,9 | SW19 |
---+------+-------------+----------+-
4 | ghi | 8,9,10,11 | SW19 |
---+------+-------------+----------+-
etc.
if I want to select an entry whose category_id contains '3' AND whose postcode starts from 'SW', what query will it be?
I just found out the solution which completely resolves my problem and though it would help someone in future.
Well, the query goes like this:
Select * from table_name where FIND_IN_SET('3', category_id) >0 AND postcode LIKE 'sw%'
Regards,
Shoaib.
:) Just simple query for this
SELECT * FROM tablename1 WHERE category_id LIKE '%,3,%' AND postcode LIKE 'SW%'

Check if a string contains numbers

How do I check if a string in a MySQL field contains a number then delete them?
Example table:
tbl_tags
-------------
| id | tag |
-------------
| 1 | hello |
| 2 | hello2 |
| 3 | 2hello |
| 4 | hel3lo |
-------------
The only way I can think of is to do each number individually and use LIKE '%1%' OR LIKE '%2%' etc. But there must be an easier way?
Check out the MySQL Regex methods. Something like the following should work.
SELECT * FROM table WHERE tag REGEXP '[0-9]'
SELECT *
FROM clients
WHERE name REGEXP '^[[:digit:]]+$'
ORDER BY `clients`.`country_id` DESC
LIMIT 0 , 30
this is the answer if you're looking for strings with only digits

Combine count rows in MySQL

I've got a table in MySQL that looks roughly like:
value | count
-------------
Fred | 7
FRED | 1
Roger | 3
roger | 1
That is, it was created with string ops outside of MySQL, so the values are case- and trailing-whitespace-sensitive.
I want it to look like:
value | count
-------------
Fred | 8
Roger | 4
That is, managed by MySQL, with value a primary key. It's not important which one (of "Fred" or "FRED") is kept.
I know how to do this in code. I also know how to generate a list of problem values (with a self-join). But I'd like to come up with a SQL update/delete to migrate my table, and I can't think of anything.
If I knew that no pair of records had variants of one value, with the same count (like ("Fred",4) and ("FRED",4)), then I think I can do it with a self-join to copy the counts, and then an update to remove the zeros. But I have no such guarantee.
Is there something simple I'm missing, or is this one of those cases where you just write a short function outside of the database?
Thanks!
As an example of how to obtain the results you are looking for with a SQL query alone:
SELECT UPPER(value) AS name, SUM(count) AS qty FROM table GROUP BY name;
If you make a new table to hold the correct values, you INSERT the above query to populate the new table as so:
INSERT INTO newtable (SELECT UPPER(value) AS name, SUM(count) AS qty FROM table GROUP BY name);
Strangely, MySQL seems to do this for you. I just tested this in MySQL 5.1.47:
create table c (value varchar(10), count int);
insert into c values ('Fred',7), ('FRED',1), ('Roger',3), ('roger',1);
select * from c;
+-------+-------+
| value | count |
+-------+-------+
| Fred | 7 |
| FRED | 1 |
| Roger | 3 |
| roger | 1 |
+-------+-------+
select value, sum(count) from c group by value;
+-------+------------+
| value | sum(count) |
+-------+------------+
| Fred | 8 |
| Roger | 4 |
+-------+------------+
I was surprised to see MySQL transform the strings like that, and I'm not sure I can explain why it did that. I was expecting to have to get four distinct rows, and to have to use some string functions to map the values to a canonical form.