How to optimize MySQL query to find substring among three fields? - mysql

I have the following MySQL table fields:
description1, description2, description3: Varchar(500)
value: int
and wish to find the records where at least one of the description includes the string searched by the user.
Right now I am using the following query. It works, but it takes about 1.5 second to return the results.
SELECT `table`.`value`,
`table`.`description1`,
`table`.`description2`,
`table`.`description3`
FROM `table`
WHERE ( `table`.`description1` LIKE '%string%'
OR `table`.`description2` LIKE '%string%'
OR `table`.`description3` LIKE '%string%' )
ORDER BY `table`.`value` DESC LIMIT 0 , 9
Is there any way to get the results faster?
(Note that the value field is already indexed).

add full text index and instead like use AGAINST

Related

optimise like `%value%` on join in exponential-growing, mysql database

i tried lots of thing but not of them worked hope someone may help me with this query
let me show my query first then issue
select log.*,client.client_name
from ( select * from sessions
where ( `report_error_status` like CONCAT('%' ,'consec', '%')
or `ipaddress` like CONCAT('%' ,'consec', '%') or `last_updated` like CONCAT('%' ,'consec', '%') )
ORDER BY `id` DESC LIMIT 10 OFFSET 0 )
log
inner join
(select * from clients
where ( `client_name` like CONCAT('%' ,'consec', '%') ) )
client on log.client_id = client.id
in order to prevent exponential reducing query speed i'm applying limit in my table session above query working perfectly fine without "where", but my problem lies over here if user from front end try to search any thing in datatable , where clause is dynamically get attached in backend (above query with where) now my problem is that suppose table (session) does not contain user search value consec ,but table (client) contain then final query still return null value now is there any way to apply conditional where like below query
ifnull((select id from sessions where
(`report_error_status` like CONCAT('%' ,'consec', '%')
or `ipaddress` like CONCAT('%' ,'consec', '%')
or `last_updated` like CONCAT('%' ,'consec', '%'))
),
(select * from sessions ORDER BY `id` DESC LIMIT 10 OFFSET 0) ))
it will resolve all my problem is there any way to achieve in mysql.
if table session contain 100 000 data it will search with client table one by one against 100k records. suppose time taken to execute is 1 sec now what if my session table has 200k data again time will increase exponentially in inner join, to avoid this i'm using subquery in session with limit
Note report_error_status,ipaddress, client_name etc in index
There is no way to optimize a MySQL SELECT statement that use a regex opening with the wildcard. Your REGEX is %consec%, and you could add an index, but to quote the official MySQL documentation...
The index also can be used for LIKE comparisons if the argument to LIKE is a constant string that does not start with a wildcard character. For example, the following SELECT statements use indexes:
SELECT * FROM tbl_name WHERE key_col LIKE 'Patrick%';
SELECT * FROM tbl_name WHERE key_col LIKE 'Pat%_ck%';
Source: Dev.MySQL.com: Comparison of B-Tree and Hash Indexes; B-Tree Index Characteristics
Your query falls outside of this use case, so indices will not help. Here's another answer suggesting the same.
I am going to suggest Database Normalization...
You're selecting fields that are LIKE %consec%. Why? What is this value? Is it a special, internal code that means something special for your software and your software alone? After all, look the names of the fields -- report_error_status, ipaddress, last_updated. Except for maybe the error code one, there's no reason "consec" would appear in these, unless it had some internal significance.
For instance, table.field has value of "userconsec", sometimes you want to search for "user", other times "consec".
In that case, you'd want a new table; "tableType", with tableType.tableid pointing to the other table and tableType.Type being the Type value ("user", "consec", etc.), an index on both tableid and Type, and then you can drop from your query WHERE LIKE ... and add instead JOIN ON tableType.tableid = table.id AND tableType.Type = "consec";.
It will be faster because...
It is not looking through all the text of several text fields.
It is looking through an ordered list of integers to identify the record you need.

Mysql subquery or something better

I am somewhat new to mysql and I am having an issue on how I should best write the following query. Say I have a table that has a datetime column as well as a few others I want to search on. Since this is just one table, I don't think a join statement would be appropriate here (but I may be wrong since I have not done much in the way of join statements) and I think a subquery is what I need here. So my initial query is to search the table based on a search string the user entered and then I want to limit that on a datetime (start date and end date) also specified by the user in an HTML form.
Table Schema
id, datetime, host, level, message
I want to select any rows that contain $searchstring first so something like ...
SELECT * FROM $table WHERE (level LIKE '%$searchstring%') OR (message LIKE '%$searchstring%') LIMIT $offset,$limit
If I want to limit the above results also by the datetime column, the query would look something like this ...
SELECT * FROM $table WHERE (datetime >='$startdate') AND (datetime < '$enddate')
How can I best merge these queries into one so I can first get any rows that match the search query and then further limit the rows by the start and end datetime?
TIA
You can achieve that by using a single where condition.
In your case:
SELECT * FROM $table WHERE ((level LIKE '%$searchstring%') OR (message LIKE '%$searchstring%')) AND (datetime >='$startdate') AND (datetime < '$enddate') LIMIT $offset,$limit
You don't have to use a JOIN but only add a condition
SELECT *
FROM $table
WHERE (level LIKE '%$searchstring%' OR message LIKE '%$searchstring%')
AND
datetime >='$startdate'
AND datetime < '$enddate'
LIMIT $offset,$limit

Use different table column for sphinx search weight sorting

Rather than the default sorting in sphinx, I would like to sort/weight results based on a field in another table. My schema looks like this:
node {
id
weight
}
node_text {
id
node_id
text
}
(Note: There is only one node_text for each node)
I want to index node_text, but be able to return sphinx results ordered by node.weight. I am assuming I need something like this:
sql_query = SELECT node_id, text from node_text
sql_joined_field = weight from query; SELECT id, weight FROM node ORDER BY id ASC
Is this the correct way to search for matches ORDER BY node.weight DESC? I would like to be able to run a query like the following:
mysql> SELECT * FROM nodetest1 WHERE MATCH('foobar') ORDER BY weight DESC; SHOW META;
sql_joined_field, makes a field, you need weight to be stored in an attribute. Easiest would be a simple join...
sql_query = select node_id, text, weight from node_text inner join node using (node_id=node.id)
sql_uint_attr = weight
That should then work for you SphinxQL query :)

mysql query with 'like' doesn't work with varchar and space

I have a Db with one table with 3 fields like the following:
user_id TimeStamp Azioni
where the 'timestamp' field is a varchar(25) like this: 2012/09/19 16:34:01.95
It is a varchar and not a timestamp value because i need it to be in the shown format.
And i cannot change its type even if i wanted to.
Now, I'm trying to get all db entries with the same date. For example, when Timestamp contains 2012/09/19
I tied several queries:
Query 0:
SELECT Azioni.Action
FROM Azioni
WHERE TimeStamp LIKE '2012/09/19%'
Query 1:
SELECT `Azioni`.*
FROM Azioni
Where `TimeStamp` LIKE '{2012/09/19}%'
Query 2:
SELECT `Azioni` . *
FROM Azioni
WHERE LOCATE( '2008/09/19', `TimeStamp` ) >0
Query 3:
SELECT `Azioni` . *
FROM Azioni
WHERE INSTR( `TimeStamp` , '2012/09/19' ) >0
Query 4:
SELECT * FROM `Azioni`
WHERE `TimeStamp` like '2012|/09|/19%' escape '|'
and I always get: MySQL returned an empty result set (i.e. zero rows).
But I am sure there are rows containing the said timestamp. What am i doing wrong? Does the 'space' between date and time create a problem? If so how can i solve it? Do you have any suggestion?
EDIT:
Aa suggested, from
SELECT TIMESTAMP, HEX( TIMESTAMP )
FROM Azioni
i get the following
2009-06-06 09:28:00.0000 323030392D30362D30362030393A32383A30302E30303030
2009-06-06 09:29:00.0000 323030392D30362D30362030393A32393A30302E30303030
2009-06-06 09:30:51.0000 323030392D30362D30362030393A33303A35312E30303030
2009-06-06 14:25:00.0000 323030392D30362D30362031343A32353A30302E30303030
2009-06-06 14:26:00.0000 323030392D30362D30362031343A32363A30302E30303030
EDIT 2:
ehm yeah, i was typing the date wrong in the query. Sigh, i'm stupid. Sorry for wasting your time guys.
How about this:
where timestamp like '2012/09/19%'
And, if you are going to call the field timestamp you should store it as a date/datetime/timestamp. Call it something else if it is going to be stored as a string. Timestamp is actually the name of a type in MySQL, so having that in a column name with a different type is quite misleading.
EDIT:
Have you tried:
where left(timestamp, 10) = '2012/09/19'
It sounds like there are string characters in the field, which are preventing reasonable code from working.
SELECT * FROM Azioni
WHERE `TimeStamp' LIKE '2012/09/19%'

I need some help getting MySql to output some results using a subquery

I'm storing a list of numbers inside a table as a varchar(255) and want to use this list in another query's "IN() clause.
Here's what I mean:
Table Data:
CREATE TABLE IF NOT EXISTS `session_data` (
`visible_portf_ids` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `session_data` (`visible_portf_ids`) VALUES
('45,44,658,659,661,45,44,658,659,661')
I want to run a query like this to return a list of portfolio's "QUERY #1":
SELECT portfolio_hierarchy_id, account_id, name, leaf_node_portf_id
FROM portfolio_hierarchy
WHERE account_id = 1
AND leaf_node_portf_id IN
(
(SELECT visible_portf_ids
FROM session_data
WHERE username = 'ronedog')
)
ORDER BY name ASC
The result of the query above returns only 1 row, when there are a total of 3 that should have been returned.
If I run the subquery alone like this:
(SELECT visible_portf_ids
FROM session_data
WHERE username = 'ronedog')
it will return a list like this:
45,44,658,659,661,45,44,658,659,661
But, when I run Query #1 above, only one row of data, which is associated with the "visible_portf_ids" of "45" is returned.
If I replace the subquery with hard coded values like this:
SELECT portfolio_hierarchy_id, account_id, name, leaf_node_portf_id
FROM portfolio_hierarchy
WHERE account_id = 1
AND leaf_node_portf_id IN (45,44,658,659,661,45,44,658,659,661)
ORDER BY name ASC
then I get all 3 rows I'm expecting.
I'm guessing that MySql is returning the list as a string because its stored as a varchar() and so it stops processing after the first "visible_portf_ids" is found, which is "45", but I'm not really sure.
Anyone got any ideas how I can fix this?
Thanks in advance.
You should think about restructuring your tables storing each value in a new row, instead of concatenating them.
Until then, you can use the FIND_IN_SET() function:
AND FIND_IN_SET(leaf_node_portf_id,
(SELECT visible_portf_ids
FROM session_data
WHERE username = 'ronedog'
LIMIT 1)
) > 0
Unfortunately MySQL does not have a function to split a delimited string. Your IN argument is a single string with the result of your subquery. The reason it works when you hard-code it is that MySQL is parsing the values.
I suggest that you redesign your data base to store the visible ports list as separate rows in a separate table. Then you can retrieve them and use them in subqueries like you tried.