MySQL fetching rows from 2 tables (not relational) with LEFT JOIN - mysql

I have the following 2 tables
table_article:
id subject tags
---------------------
1 subject-1 2,4,5
2 subject-2 3,5
3 subject-3 1,2
4 subject-4 2,3,4
5 subject-5 3
table_tags:
id tag_name
---------------------
1 php
2 jQuery
3 css
4 mysql
5 java
and I'm trying to get results like
id => 1, subject => subject-1, tag_names => jQuery,mysql,java
id => 2, subject => subject-2, tag_names => css,java
id => 3, subject => subject-3, tag_names => php,jQuery
Here is my current attempt, which returns ONLY the first tag (e.g. 2 instead of 2,4,5 for row 1)
1 SELECT
2 table_article.id,
3 table_article.subject,
4 GROUP_CONCAT(table_tags.tag_name) AS tag_names
5 FROM
6 table_article
7 LEFT JOIN
8 table_tags
9 ON
10 (table_tags.tag_id IN (table_article.tags))
11 GROUP BY
12 table_article.id
13 LIMIT
14 3
and the results are
id => 1, subject => subject-1, tag_names => jquery
id => 2, subject => subject-2, tag_names => css
id => 3, subject => subject-3, tag_names => php
The problem occurs on line 10 -> IN (table_article.tags).
I just can't figure out how could I solve this problem, can anyone help please?

You can't use a string that happens to contain commas as a list of discrete values.
In other words this:
ON table_tags.tag_id IN (2,4,5)
Is not the same as this:
ON table_tags.tag_id IN ('2,4,5')
The numeric value of a string like '2,4,5' is the initial numeric portion, and the remainder after the first non-numeric character is ignored. So the string '2,4,5' has a numeric value of 2. It won't be an error, but it won't get you what you intended, which is a match against any of the values in the comma-separated list.
MySQL has a built-in function FIND_IN_SET() which does understand strings that contain comma-separated values. The function returns the position of the matching value, or 0 if no match was found.
ON FIND_IN_SET(table_tags.tag_id, '2,4,5') > 0
But this cannot use an index and it forces you to run a table-scan which is going to kill your performance. To be clear, I don't recommend using this function in a join condition.
The answer is: Don't store tags in a comma-separated list. See my answer for Is storing a comma separated list in a database column really that bad?
Store one tag per row in a separate table, as #Martin Lyne suggests. That way you can look for the right tag with = and you can even index the column for much better performance.

I've not seen an IN in a ON before (not saying it's not valid) but I would do ON table_tags.tag_id = table_article.tags)
So you end up with multiple rows
subject-1, query
subject-1, css
subject 2, query
then the GROUP BY would compress the table and the GROUP_CONCAT gets all the missing tags.

Well, I would use IN in this situation, it won't work, replace it with FIND_IN_SET(table_tags.tag_id, table_article.tags) > 0 and you'll be fine. Though you really should normalize this.

As other said, this is not a good design.
Instead, you could change your table design this way:
table_article
id
subject
article_tag
article_id
tag_id
table_tags
id
tag_name
Life would be much easier this way :-)

Related

How to properly format overlapping mySQL IN and NOT IN conditions

I have the following mySQL table:
data
1
2
3
4
5
6
7
8
9
I would like to supply my select statement with two seperate lists
Exculde List:
1,4,5,7
Include List:
1,2,3,4,5,6,7
I tried the following statement:
Select * FROM table WHERE data NOT IN ('1,4,5,7') AND data IN ('1,2,3,4,5,6,7)
Expecting the following output:
data
2
3
6
But I received no results. I realize I passed an impossible condition but I don't know how to format my query to return the expected results.
Can anyone tell me what I'm doing wrong here?
IN takes a list of values, not a string that holds a delimited list of values.
Examples:
x IN (1, 2, 3)
x IN ('a', 'b', 'c')
Use IN (1,2,3) and not IN ('1,2,3') as the former compares to individual values 1, 2 and 3 while the latter is against the literal string 1,2,3.
Select * FROM ( (Select * FROM table WHERE data NOT IN ('1,4,5,7') ) AS table WHERE data IN ('1,2,3,4,5,6,7)
you try againt

MYSQL - Find rows, where part of search string matches part of value in column

I wasn't able to find this anywhere, here's my problem:
I have a string like '1 2 3 4 5' and then I have a mysql table that has a column, let's call it numbers, that look like this:
numbers
1 2 6 8 9 14
3
1 5 3 6 9
7 8 9 23 44
10
I am trying to find the easiest way (hopefully in a single query) to find the rows, where any of the numbers in my search string (1 or 2 or 3 or 4 or 5) is contained in the numbers column. In the give example I am looking for rows with 1,2 and 3 (since they share numbers with my search string).
I am trying to do this with a single query and no loops.
Thanks!
The best solution would be to get rid of the column containing a list of values, and use a schema where each value is in its own row. Then you can use WHERE number IN (1, 2, 3, 4, 5) and join this with the table containing the rest of the data.
But if you can't change the schema, you can use a regular expression.
SELECT *
FROM yourTable
WHERE numbers REGEXP '[[:<:]](1|2|3|4|5)[[:<:]]'
[[:<:]] and [[:<:]] match the beginning and end of words.
Note that this type of search will be very slow if the table is large, because it's not feasible to index it.
Here is a start point (split string function) : http://blog.fedecarg.com/2009/02/22/mysql-split-string-function/ := SplitString(string,delimiter,position)
Create a function so it converts a string to an array := stringSplitted(string,delimiter)
Create a function so it compares two arrays :=arrayIntersect(array1, array2)
SELECT numbers
FROM table
WHERE arrayIntersect(#argument, numbers)
Two function definitions with loops and one single query without any loop
SELECT * FROM MyTable WHERE (numbers LIKE '%1%' OR numbers LIKE '%2%')
or you can also use REGEX something like this
SELECT * FROM events WHERE id REGEXP '5587$'

MySQL combine strings from many rows like SUM for int

I'm searching function which it's working like sum.
For simple I have 4 records in table:
id => 1
text => abc
id => 2
text => def
id => 3
text => ghi
id=> 4
text => jkm
When I used SUM() for ids my result is 10, so I would like to find another function for combine texts and I want to to have "abcdefghijkm" or, is it possible, "abc.def.ghi.jkm".
I'm trying with FOR XML PATH() but it isn't working with MySQL (I can't use this... function?). Also CONCAT() isn't working too.
Use GROUP_CONCAT() function:
SELECT SUM(id), GROUP_CONCAT(text SEPARATOR '.') FROM t

MYSQL - SELECT ALL FROM TABLE if

I have a (nice) mysql table built like this :
Fields Datas
id (pk) 1 2 3 4 5 6
master_id 1000 1000 1000 2000 2000 2000 ...
master_name home home home shop shop shop ...
type_data value common client value common client ...
param_a foo_a 1 0 bar_a 0 1 ...
param_b foo_b 1 0 bar_b 1 0 ...
param_c foo_c 0 1 bar_c 0 1 ...
... ... ... ... ... ... ... ...
All these datas are embed in a single table. Each datas are dispatched on 3 "columns" set (1 for the values, 1 for identifying if these are common values and one for identifying client values). It's not the best I got but many other scripts depends on this structure.
I'd need sthg like this:
SELECT parameters name (eg param_a, param_b..) and their values (eg foo_a, foo_b..)
WHEN master_id=? AND type_data=(common or client) (eg for values=1 on the 2nd column)
.
in order to get the parameters hash for a particular master_id like
param_a => foo_a
param_b => foo_b
param_c => foo_c
...
I could not succeed in self joining on the same table till now but I guess it should be feasible. (I'd like to avoid to do several queries)
Thx in advance
I think you are talking about pivoting the data? If so, see here: http://en.wikibooks.org/wiki/MySQL/Pivot_table and here: http://dev.mysql.com/tech-resources/articles/wizard/print_version.html

MySQL Inner Query with a Where and Group by conditions in cakephp

i am having a table named Reports with
id report_id user_id
1 1 5
2 1 5
3 1 5
4 2 5
5 2 5
6 3 6
7 3 6
8 4 1
9 4 1
10 4 1
i am trying to write a Query such that user_id = 5 and to find how many reports he has created.(Answer should be of 2 )
i have a Wrote a Mysql Query as
select count(distinct report_id) from Reports where user_id=5
i m trying the same MYSQl sub Query inside the Foreach users loop where my 5 is from $user['User']['id'];
how to write the MYSQL Query above inside this for loop in cakephp Framework....
foreach($users as & $user):
echo "User id ".$user['User']['id'];
$user['User']['report_count'] = $this->Report->find('count',
array('conditions'=>array('Report.user_id'=>$user['User']['id'])));
endforeach;
$this->set('users', $users);
Please suggest me.......HOw to write the above Mysql Query in cakephp
You want to use the following functions GROUP BY and COUNT
Your query could look somewhat like this
select count(distinct report_id) from Reports where user_id=5
If this is a list of users you are showing in your application... you could significantly reduce the number of queries you are running.
eg. for 100 users you will be running 100 queries instead you can run a single single query to extract the user_id and count of reports by each user
select count(distinct report_id) as count,user_id from Reports where user_id IN (1,2) GROUP BY user_id;
OR if you want to run seperate queries for each user
select count(distinct report_id) as count,user_id from Report where user_id=5;
Try this:
$user['User']['report_count'] = $this->Report->find('count',
array( 'conditions' => array('Report.user_id' => $user['User']['id']),
'fields' => 'DISTINCT Report.report_id'
)
);
It should fetch all distinct report_ids for a given user_id, then count them. Basically, it should run the query:
SELECT DISTINCT report_id FROM Reports WHERE user_id=$user['User']['id']
(after substituting the value of $user['User']['id']), then count the number of rows in the result. Caveat: I don't use CakePHP in real life, I just read the documentation; your mileage may vary. As halocursed mentions, running a single SQL query on your own would be more efficient than calling find(...) for each user ID. You could also try:
$report_counts = $this->Report->find('list',
array( 'conditions' => array('Report.user_id' => array_map(create_function('$user', 'return $user["User"]["id"];'), $users)),
'group' => array('Report.user_id'),
'fields' => array('Report.user_id', 'COUNT(DISTINCT Report.report_id) AS report_count')
)
);
foreach ($users as &$user) {
$user['User']['report_count'] = $report_counts[$user['User']['id']];
}
However, I don't know if CakePHP will accept aggregate functions in the 'fields' parameter, and I don't know as though find('list', ...) will pick Report.user_id as the array index. If you're having problems with the latter, you could switch to a [find('all', ...)][3] call and loop over $report_counts rather than $users. I didn't take this approach because I don't know the structure of $users, such as how it's indexed.