mysql group by comma separated values - mysql

I have a mysql table comments in that table a field is tags and all tags are comma separated like
Comments Table
Name Reply Tags
a b new,old,facebook,fb
b d water,faebook,wall
b r wall,php,fb
c q name,facebook,email
a y weather,cold,old
a w twitter,next,pet,fb
I need to get most used tag in this table with mysql query..

I think that the best approach is to use a separate table for Tags and for the relation between Tags and your table (i.e.: CommentTags), it'll allow you to quickly find those comments that use any speciffic tag, do statistics, etc. With your current approach, you end up using a string splitting for each row, each time.
Now, if you are stuck with your current approach, here is a question about string splitting that you may find useful. But I foresee a great deal of performance complains by users...

Related

In MySQL WHERE clause, how can I check for each item in a comma separated list against another comma separated list that may be ordered differently

I'm trying to write a query to look-up rows which contain one or more of comma-separated values. Please see scenario below:
Table: Events, target row to lookup is called "courses"
The data in the courses column is a comma separated string of course codes.
ABC123,XYZ345,ABC987
ABC123
ABC123,ABC987
XYZ345,ABC123
Now what I want to do is to do a
Select * from events where courses in ("ABC123","XYZ345");
However this returns no results.
What I want is that the where lookup to get me all rows that CONTAINS ABC123 OR XYZ345.
Could someone please help me do this?
You could use like, the % are wildcards.
select *
from events
where courses like "%ABC123%" or courses like "%XYZ345%"
There are other approaches: https://dev.mysql.com/doc/refman/8.0/en/pattern-matching.html
It might also be worth normalising the data so that each value is stored in its own row in another table rather than in a comma separated string, as using like will start to hurt performance as the table grows.
#user3783243 mentions loose matching which is a good point and just one reason why storing data like this isn't the best approach.
One way around it would be to tweak the query above to something like:
select *
from events
where courses like "%ABC123,%" or courses like "%XYZ345,%"
but this poses another problem, are the comma separated values always split by a single comma (ABC123,XYZ345) or is there any whitespace (ABC123, XYZ345).
Another problem pointed out by #GarethD is that the previous approaches won't match the last value of the comma separated string (unless it does have a trailing comma). One way I can think of is to do something like this, but it starts making the query a bit clunky, and also assumes all values are 6 characters in length, at this point, it might be worth using a regular expression.
select *
from events
where courses like "%ABC123,%" or courses like "%XYZ345,%"
or right(courses, 6) = 'ABC123' or right(courses, 6) = 'XYZ345'
If all values are indeed six characters then it might be worth trying other mysql functions such as locate, substring and regexp to try and simplify the query.
Just use FIND_IN_SET()
SELECT columns FROM events WHERE FIND_IN_SET("ABC123", courses) OR FIND_IN_SET("XYZ345", courses)
Better of course would be a normalized table, since you can't use indexes with the current design.

How to represent Cross Apply and Split String in MySQL

For some background, what i'm trying to do create a database that contains multiple recipes. However, it's necessary for the individual ingredients to be linked to the recipe they originally came from.
For instance, I have table containing all the individual ingredients.
And a table where the recipes are stored, minus the ingredients.
Now, i've found this article which covers how to split strings using T-SQL XML Commands, and the code that is used to do so is:
SELECT
Books.BookId,
Books.Book,
BookAuthors.AuthorId,
BookAuthors.Author
FROM Books
CROSS APPLY dbo.split(Books.Authors,',') split
INNER JOIN BookAuthors ON BookAuthors.AuthorId = split.val
The result i'm looking for would be very similar to this:
However, CROSS APPLY etc only works on MS SQL Server and my question is:
Is it possible to achieve the same, or very similar effect using MySQL?
Thanks for any help.
This should quite match what you're trying to get:
SELECT
Books.BookId,
Books.Book,
BookAuthors.AuthorId,
BookAuthors.Author
FROM Books
LEFT JOIN BookAuthors ON (find_in_set(BookAuthors.AuthorId, Books.Authors) <> 0)
Found this article very helpful: MySQL query finding values in a comma separated string
Leave a comment if you need further explanation how it works.

MySQL CONCAT '*' symbol toasts the database

I have a table used for lookups which stores the human-readable value in one column and a the same text stripped of special characters and spaces in another. e.g., the value "Children's Shows" would appear in the lookup column as "childrens-shows".
Unfortunately the corresponding main table isn't quite that simple - for historical reasons I didn't create myself and now would be difficult to undo, the lookup value is actually stored with surrounding asterisks, e.g. '*childrens-shows*'.
So, while trying to join the lookup table sans-asterisks with the main table that has asterisks, I figured CONCAT would help me add them on-the-fly, e.g.;
SELECT *
FROM main_table m
INNER JOIN lookup_table l
ON l.value = CONCAT('*',m.value,'*')
... and then the table was toast. Not sure if I created an infinite loop or really screwed the data, but it required an ISP backup to get the table responding again. I suspect it's because the '*' symbol is probably reserved, like a wildcard, and I've asked the database to do the equivalent of licking its own elbow. Either way, I'm hesitant to 'experiment' to find the answer given the spectacular way it managed to kill the database.
Thanks in advance to anyone who can (a) tell me what the above actually did to the database, and (b) how I should actually join the tables?
When using CONCAT, mysql won't use the index. Use EXPLAIN to check this, but a recent problem I had was that on a large table, the indexed column was there, but the key was not used. This should not bork the whole table however, just make it slow. Possibly it ran out of memory, started to swap and then crashed halfway, but you'd need to check the logs to find out.
However, the root cause is clearly bad table design and that's where the solution lies. Any answer you get that allows you to work around this can only be temporary at best.
Best solution is to move this data into a separate table. 'Childrens shows' sounds like a category and therefore repeated data in many rows. This should really be an id for a 'categories' table, which would prevent the DB from having to run CONCAT on every single row in the table, as you could do this:
SELECT *
FROM main_table m
INNER JOIN lookup_table l
ON l.value = m.value
/* and optionally */
INNER JOIN categories cat
ON l.value = cat.id
WHERE cat.name = 'whatever'
I know this is not something you may be able to do given the information you supplied in your question, but really the reason for not being able to make such a change to a badly normalised DB is more important than the code here. Without either the resources or political backing to do things the right way, you will end up with even more headaches like this, which will end up costing more in the long term. Time for a word with the boss perhaps :)

What do the dots mean in this SQL query?

I'm new to MySQL. Can anyone describe lines below which I get theme from the demo of the jqgrid, what is the meaning of a.id? What is the meaning of these dots?
$SQL = "SELECT a.id, a.invdate, b.name, a.amount,a.tax,a.total,a.note FROM invheader a, clients b WHERE a.client_id=b.client_id ORDER BY $sidx $sord LIMIT $start , $limit";
You can find the example here:
http://trirand.com/blog/jqgrid/jqgrid.html
in the advanced>Multi select
You've asked several questions here. To address the dots:
In the FROM clause, a is used as an alias for the invheader table. This means you can reference that table by the short alias a instead of the full table name.
Therefore, a.id refers the the id column of the invheader table.
It is generally considered bad practice to simply give your tables the aliases a, b, c, etc. and I would recommend you use something more useful.
I suggest you read some basic MySQL tutorials as this is a fundamental principal.
The dot(.) is used to separate the board scope.So Songs.songId mean that first find the table named Songs and then in the Songs table find the field named songId.
In my opinion, that DOT NOTATION is used for fetching information from right side of the syntax. That means, a.id which means you fetch the data from "a table". In this case, you use the aliases name, then it runs '.id'which means it fetches data 'ID'from a table.If it is wrong, please comment that wrong statement. thank you

SQL Match Against or Some other method to search two tables

I've look at the questions on here but can't find an answer that specifically matches what i'm trying to achieve...
Basically I have an SQL table that stores FAQ information. The table in question are below with the primary keys in bold.
Table: questions,
Fields: id_question, question, answer, status, etc......
Table: question_tags,
Fields: id_question, tag
Table: tags,
Fields: tags
The question_tags table is essentially a link between the question table and the tag table (i.e. many to many).
What I am looking to do is implement something like SQL MATCH AGAINST so when a user enters a question into the search box (PHP) the system looks up the questions table and searches the questions for the keywords in the search term. I can get this to work but what I want to be able to do is include the tags within the search. The reason for this is that I believe there are a number of different ways the same question can be using different words. The tags are supposed to compliment the question when searching. I understand that this is impossible using the MATCH AGAINST function within SQL so i'm kind of stuck.
Is there a way around this limitation?
Or instead of having each tag in a separate column of a separate table (linked with a third table) as above, should I simply create another field in the questions table for the entire tag string?
Or does anyone else have any other suggestions?
The solution will not be working with large datasets to begin with but it would be helpful if the solution was scalable.
Thanks
Tags may be a good candidate for de-normalization for this use case.
One possibility would be something like
SELECT * FROM
(
SELECT Question, Answer, Status, MATCH(title,category) AGAINST ('keyword') as rank
FROM questions
WHERE MATCH(Question,Answer) AGAINST ('Keyword')
UNION
SELECT Question, Answer, Status, MATCH(Tag) AGAINST ('keyword')
FROM questions Q
INNER JOIN question_tags QT
ON Q.id_question = QT.id_question
INNER JOIN tags T
ON QT.id_tag = T.id_tag
WHERE MATCH(Tag) AGAINST ('keyword')
) R
ORDER BY rank
Basically doing a FT search against question/answer and another on tags and unioning the resultset and then sorting by rank.