MySQL query - how to look for certain string in the field - mysql

I have a table "story" as follows:
+++++++++++++++++++++++++++++++++++++++++++
| id | keywords |
+++++++++++++++++++++++++++++++++++++++++++
| 1 | romance,movie,drama |
| 2 | newmovie,horor,comedy |
| 3 | movie,scifi |
| 4 | newmovie,romance,drama,asia |
| 5 | kids,movie |
+++++++++++++++++++++++++++++++++++++++++++
I try a query to search 'movie' in keywords field as below:
SELECT id FROM story WHERE keywords LIKE '%movie%'
and the result is
1,2,3,4,5
but in this case I wanted the result is 1,3,5 (field value with newmovie not include). Can someone help me how the query to do it?
Thank you for your help..

You want to use find_in_set like this:
SELECT id FROM story WHERE find_in_set('movie', keywords) > 0;
Though you should really consider normalizing your table structure.
In this case, you could've stored one single keyword in one row, then the query would be simply like:
select id from story where keyword = 'movie';
and that would've been the end of it. No heavy string functions needed.
You could have structure like this:
keywords(id, name);
story(story_id,. . ., keyword_id);
then, you could simply join the two like this:
select s.*
from story s
inner join keywords k on s.keyword_id = k.id
where k.name = 'movie';

Your problem is that "newmovie" can be found by "%movie%" you need only search "movie".

Related

Search part of a string in a record with multiple strings

I have the following record in artists table:
| id | names |
| 1 | eminem rihanna rita ora|
| 2 | taylor swift beyonce |
I want to search for example using inem and I want the id of this record to be found which is id = 1 in this case. I'm using Full Text Search in MySQL.
Is there a better technique to achieve this?
Update:
| id | video_name | title | artists | search_tags |
| 1 | onajr | o'najr | kida | onajr o'najr kida |
I want to search using this strings for ex. onajr, ona, kida, kid.
So in e few words a user can search using al the search tags including part of a tag.
This is my function in php. :
public function tags_search() {
//echo 'ok';
$db = DB::get_instance();
//single word
$query = "
select * from `videos`
where
match(`search_tags`) against (':video_name' IN BOOLEAN MODE) order by rand()
limit 1
";
try {
$run_query = $db->prepare($query);
$run_query->execute(array(
':string' => $this->string
));
return ($run_query->rowCount() == 1) ? $run_query->fetch(PDO::FETCH_OBJ)->video_name : false;
} catch(PDOException $e) {
echo $e->getMessage();
}
}
You should be able to just use LIKE
SELECT id FROM artists WHERE names LIKE '%inem%';
The %'s are wild cards saying that anything can come before or after them.
Also, it's generally not a good idea to store multiple values in a single field. I'd recommend making id and names into a composite PK, and then only have one name per field.
eg)
| id | name |
| 1 | eminem |
| 1 | rihanna |
| 1 | rita ora |
| 2 | taylor swift |
| 2 | beyonce |
If you plan on adding more fields, they should be in another table. Learn about database normalization for the reason why.
Edit after update:
I still think a LIKE statement would suit your purposes, the only thing you would have to do different is add a AND search_tags LIKE '%ona%' etc for each comma delineated tag word.
Just as an example from what you have above:
SELECT id FROM artist
WHERE search_tags LIKE '%onajr%'
AND search_tags LIKE '%ona%'
AND search_tags LIKE '%kida%'
AND search_tags LIKE '%kid%';
I don't know enough about PHP to actually give you some code, but after a short search, explode() appears to be the function you want to look at. Then just append the extra SQL for each search tag. This method would also work if you were to split your tags into another table. If I'm not mistaken, you should just have to group by the songid if you go that route.
Not sure if all this is better than what you have. As I said, I don't know much PHP, and I haven't messed around with match(...) against(...), but I thought I'd throw in my own 2¢.

Tag Database Schema

I have three tables for tagging. The first one is the question table that has a list of question with certain ID. The second one is the tag table that has a list of tag name with certain ID. And the third one is the question_tag table, a collection of question to a tag. A question that has multiple tag means multiple rows in question_tag, I thought of storing an serialized array into the question_tag table but it's in general not a good idea to store array inside of a SQL database.
Below is the schema. Arrow denoting foreign key.
-------------------
----------------- | question_tag |
| question | ------------------- ------------------
----------------- | question_tag_ID | | tag |
| question_ID | ---> | question_ID | ------------------
----------------- | tag_ID | <----- | tag_ID |
------------------- | tag_name |
------------------
I want to make a query that will output this table below.
----------------------------------------------------
| question_id | tag_name |
----------------------------------------------------
| 1 | algebra, calculus, differentiation |
| 2 | calculus |
| 3 | algebra, trigonometry |
----------------------------------------------------
How do I manage to do this query? I thought about SELECTING from question and JOINING a temporary table of SELECT tag.tag_name FROM tag WHERE question_tag.tag_ID = tag.tag_ID, but how do I output this RIGHT column (tag_name) like the table above.
I would really appreciate it if you can help me with this SQL query, I am guessing that I need to do a nesting SELECT query for the RIGHT (tag_name) column, then JOIN it to the question_table. But I am not sure how to the nesting of SELECT query.
This is what I have come up with:
SELECT * FROM question as Q LEFT JOIN (SELECT T.tag_name FROM tag as T WHERE T.tag_id IN (SELECT QT.tag_id FROM question_tag AS QT WHERE QT.question_ID = Q.id)) AS QT_T
You'll need to aggregate and concatenate. Please check out some of the related questions:
Does T-SQL have an aggregate function to concatenate strings?
Implode type function in SQL Server 2000?
Concatenate row values T-SQL
How to use GROUP BY to concatenate strings in MySQL?
Aggregate String Concatenation in Oracle 10g
Create a delimitted string from a query in DB2
How to concatenate strings of a string field in a PostgreSQL 'group by' query?
UPDATE
While I don't have a mysql DB to test this on, going off of one of the above links (with the knowledge this is mysql), I've designed the following query:
SELECT
qt.question_id,
GROUP_CONCAT(t.tag_name SEPARATOR ',')
FROM question_tag qt
LEFT JOIN tag t ON t.tag_id = qt.tag_id
GROUP BY qt.question_id;
Please try this out and let me know if it works.

mysql count votes optimization

so im making a file hub nothing huge or fancy just to store some files that may be shared by others for download. and it just occured to me in the way that i originally intended to count the amount of upvotes or downvotes the query could be server heavy.the query to get the files is something along the lines of
select*from files;
and in such i would recieve an array of my files that i could loop over and get specifics on each file now with the inclusion of voting a file that same foreach loop would include a further query that would get the count the amount votes a file would get (the file id in the where clause) like so
select*from votes where upvoted=true and file.id=?
and i was thinking of using pdo::rowCount to get my answer. now evey bone in my body just says this is bad very bad as imagine im getting 10,000 files i just ran 10,000 extra queries one on each file and i havent looked at the downvotes yet which i was think could go in a similar fasion. any optimization adviece here is a small rep of the structure of a few tables. the upvoted and downvoted columbs are of type bool or tinyint if you will
table: file table: user table: votes
+----+-------------+ +----+-------------+ +--------+--------+--------+--------+
| id |storedname | | id | username | |file_id | user_id| upvoted | downvoted
+----+-------------+ +----+-------------+ +--------+--------+--------+--------+
| 1 | 45tfvb.txt | | 1 | matthew | | 1 | 2 | 1 | 0
| 2 |jj7fnfddf.pdf| | 2 | mark | | 2 | 1 | 1 | 1
| .. | .. | | .. | .. | | .. | .. | .. | ..
there are two ways to do this. the better way to do this (aka faster) is to write separate queries and build into one variable in your programming language (like php, python.. etc.)
SELECT
d.id as doc_id,
COUNT(v.document_id) as num_upvotes
FROM votes v
JOIN document d on d.id = v.document_id
WHERE v.upvoted IS TRUE
GROUP BY doc_id
);
that will return your list of upvoted documents. you can do the same for your downvotes.
then after your select from document do a for loop to compare the votes with the document by ID and build into a dictionary or list.
The second way to do this which can take a lot longer at runtime if you have a bunch of records in the table (its less efficient, but easier to write) is to add subquery selects in your select statement like this...
SELECT
logical_name ,
document.id ,
file_type ,
physical_name ,
uploader_notes ,
views ,
downloads ,
user.name ,
category.name AS category_name,
(Select count(1) from votes where upvoted=true and document_id=document.id )as upvoted,
(select count(1) from votes where upvoted=false and document_id=document.id) as downvoted
FROM document
INNER JOIN category ON document.category_id = category.id
INNER JOIN user ON document.uploader_id = user.id
ORDER BY category.id
Two advices:
Avoid SELECT * especially if you're going to count. Replace it, with something like that:
SELECT COUNT(1) AS total WHERE upvoted=true AND file.id=?
Maybe you want to create a TRIGGER to keep update a counter in the file table.
I hope it will be helpfull to you.

Selecting rows based upon a search string or any of its synonyms

I need some help please...
I have 2 tables, one contains a description field which is entered freehand by the user, the second table is made up of 2 columns, the first is a group name and the second is a list of synonyms. So, for example, I might have three rows in the synonyms table in a group called A that contains the synonyms 'Leaflet', 'Brochure', 'Hand Bill'.
What I need to do is return all rows from the first table where the ItemDescription column contains any of the synonyms of the query variable which might be 'Leaflet'.
So this should give me all of the rows that contain anywhere in the long description field the words 'Leaflet', 'Brochure' or 'Hand Bill'.
I have been able to do this only where the ItemDescription field contains only actual words being looked for, in reality this os a long wordy column that may contain 50 or 60 words any one of which may be one of the search word or any of its synonyms.
All help gratefully received as always.
Thanks.
You should probably try to use LIKE or RLIKE to match the description column. In this case, you want to match a number of alternatives, so I'll just show an example.
Let us assume that we have this table containing synonyms. Note that we have added the word itself as a synonym:
+---------+-----------+
| word | synonym |
+---------+-----------+
| leaflet | leaflet |
| leaflet | brochure |
| leaflet | hand bill |
| skin | skin |
| skin | leather |
| skin | hide |
+---------+-----------+
You don't give an example table, so I invented one called items:
+---------+-------------------+-----------------------------------+
| item_id | brief | description |
+---------+-------------------+-----------------------------------+
| 1 | Diamond | This brochure is glossy and shiny |
| 2 | Halloween Special | A leaflet for the Halloween |
| 3 | Pumpkin | This is just a Halloween pumpkin |
+---------+-------------------+-----------------------------------+
Now, we assume that you want to look for all rows containing one of the synonyms of 'leaflet' in the description. The following query does the job:
SELECT * FROM items
WHERE description RLIKE (
SELECT
CONCAT('.*(', GROUP_CONCAT(synonym SEPARATOR '|'), ').*')
FROM synonyms
WHERE word = 'leaflet'
GROUP BY word
);
The inner select create a regular expression matching one of the synonyms, and the outer select applies this regular expression to the description column of our items table.
Thanks for the feedback. I have found an answer to my SQL needs:
SELECT *
FROM MainTable a
WHERE EXISTS
(
SELECT 1
FROM (
Select concat('%',Synonym,'%') As cond
From synonyms
Where Synonym Like '%SearchString%'
OR ListRef = ( Select ListRef
From synonyms
Where Synonym Like '%SearchString%')
) с
WHERE a.Description LIKE cond
)
OR ItemDescription Like '%SearchString%'
Without the final OR I was only returning rows where something existed in the synonyms table for my search string, with the OR it also returns all straight matches not found through synonyms.

MySQL - Merge two join results

I need help with a raw SQL query which gets a value based on another value.
I have the following raw SQL query.
SELECT pmPropDef.id, pmPropDef.name, pmPropDef.units, pmPropShort.str, pmPropLong.str
FROM pmProp INNER JOIN pmPropDef ON pmProp.propid=pmPropDef.id AND pmPropDef.name = 'Area'
LEFT JOIN pmPropShort ON sid=pmProp.value
LEFT JOIN pmPropLong ON lid=-pmProp.value
WHERE pmProp.ownertype='variant' AND pmPropDef.id = pmProp.propid;
And this results in the following:
+----+------+-------+------+------+
| id | name | units | str | str |
+----+------+-------+------+------+
| 14 | Area | mm2 | 1.1 | NULL |
+----+------+-------+------+------+
The problem that I am getting both pmPropShort.str and pmPropLong.str and I should be betting one or the the other. What I really want is a single str value? How do I re-write this query to meet my needs?
You can use COALESCE which returns the first non-NULL argument. eg.
COALESCE(pmPropShort.str, pmPropLong.str)
Do you have an obvious priority?
example if long is priority
SELECT pmPropDef.id, pmPropDef.name, pmPropDef.units,
IF(pmPropLong.str IS NOT NULL,pmPropLong.str,pmPropShort.str) as str
FROM ...rest of your query