Select rows from a table where all values of an array exist - mysql

I have this query that retreives a list of id's + NAME:
$sql = "SELECT id FROM #__table1 ";
$sql .= " WHERE (";
foreach($explode_tags as $k=>$explode_tag) {
$sql .= "name = ".$db->Quote(trim($explode_tag));
if(($k+1) != count($explode_tags))
$sql .= " OR ";
}
$sql .= ")";
$db->setQuery($sql);
$results = $db->loadResultArray();
The result is an array like this:
keywordID | NAME
1 cat
2 dog
3 horse
Now I have this table2:
id | ItemID | keywordID
1 4 1
2 4 2
3 4 3
4 6 1
5 6 2
6 7 1
I want to find from table2 all ItemID's that have all keywordID's found in table1.
In the example above I want to return only itemID 4 that has all keywords (all 3 of them).
I am running this query but I am not getting results:
...
$query .= " AND i.id IN (SELECT itemID FROM #__table2 WHERE (";
foreach($results as $k=>$result) {
$query .= "keywordID = ".(int)$result;
if(($k+1) != count($results))
$query .= " AND ";
}
$query .= "))";

UPDATE
Sorry, I miss read your question. I've done simple test using this data:
id itemId keywordId
1 4 1
5 4 2
6 4 3
7 5 2
8 5 3
9 6 1
10 6 2
11 6 3
12 7 3
13 9 3
14 9 2
15 9 1
and using this query:
SELECT itemId, GROUP_CONCAT( keywordId ORDER BY keywordId ) AS crpcnct, COUNT( itemId )
FROM `temporary_table_123`
GROUP BY 1
HAVING crpcnct = '1,2,3'
I can get the value that you wanted:
itemId crpcnct count(itemId)
4 1,2,3 3
6 1,2,3 3
9 1,2,3 3
To achieve this, all you have to do is build the keywordID you want to use:
$keywordIds[] = $results['keywordId'];
and then sort accending
sort($keywordIds);
the last step is, supply this array into query:
SELECT itemId, GROUP_CONCAT( keywordId ORDER BY keywordId ) AS crpcnct, COUNT( itemId )
FROM `temporary_table_123`
GROUP BY 1
HAVING crpcnct = '" . implode(",", $keywordIds) . "'
There you have it.

SELECT ItemID FROM table2 WHERE keywordID IN (SELECT keywordID FROM table1)
This might work. I'll have to create local copies of your tables to see for sure, though.

$array_names = array();
foreach($explode_tags as $k=>$explode_tag){
$array_names[] = 'name = ' . $db->Quote(trim($explode_tag));
}
$sql = "SELECT ItemID FROM table_2 WHERE keywordID IN (SELECT keywordID FROM table_1 WHERE " . implode(' OR ', $array_names) .")";
$db->setQuery($sql);
$results = $db->loadResultArray();

Related

How to query rows from 2 tables in a single query?

TABLE 1
student_id | name
-----------------
1 | A
2 | B
3 | C
4 | D
TABLE 2
vote_id | student_id | vote_for
------------------------------
1 | 1 | 2
2 | 1 | 3
3 | 2 | 1
4 | 1 | 4
How can I get records from TABLE 1 (student names B C D) based on student_id in TABLE 2 (student_id 1) in a single query? I managed to do it but in multiple queries like below:
$students = array();
$query = "SELECT vote_for FROM table2 WHERE student_id=?";
$stmt = $this->con->prepare($query);
$stmt->bind_param("i",$student_id);
$stmt->execute();
$stmt->bind_result($vote_for);
$votes = array();
while($stmt->fetch()){
$votes[] = $vote_for;
}
$stmt->close();
if (!empty($votes)) {
$query = "SELECT name FROM table1 WHERE student_id=?";
foreach ($votes as $vote) {
$stmt = $this->con->prepare($query);
$stmt->bind_param("i",$vote);
$stmt->execute();
$stmt->bind_result($name);
while($stmt->fetch()){
$temp = array();
$temp['name'] = $name;
$students[] = $temp;
}
$stmt->close();
}
}
You can use a JOIN query to get the names of the students that were voted for by a given student_id. For example:
SELECT s.name AS voted_for
FROM table2 v
JOIN table1 s ON s.student_id = v.vote_for
WHERE v.student_id = 1
Demo on dbfiddle
In PHP:
$students = array();
$query = "SELECT s.name AS voted_for
FROM table2 v
JOIN table1 s ON s.student_id = v.vote_for
WHERE v.student_id = ?";
$stmt = $this->con->prepare($query);
$stmt->bind_param("i",$student_id);
$stmt->execute();
$stmt->bind_result($name);
while($stmt->fetch()) {
$students[] = array('name' => $name);
}
$stmt->close();
I believe you can achieve it with the following query:
SELECT T1.STUDENT_ID, T1.NAME
FROM TABLE_1 T1, TABLE_2 T2
WHERE T1.STUDENT_ID = T2.VOTE_FOR
AND T2.STUDENT_ID = ?
And you would just inject the STUDENT_ID for the Table 2. You can remove the T1.STUDENT_ID if necessary.

LIMIT MySQL subquery using variable value before LEFT JOIN

I have this MySQL table posts:
id | content | parentid | userid
--------------------------------------
01 | Post test #1 | 0 | 1
02 | Post test #2 | 0 | 1
03 | Comment #1 | 1 | 2
04 | Comment #2 | 1 | 1
05 | Post test #3 | 0 | 3
06 | Comment #3 | 1 | 2
07 | Comment #4 | 2 | 5
08 | Comment #5 | 5 | 6
09 | Comment #6 | 1 | 4
10 | Post test #4 | 0 | 4
This is just an example for stackoverflow
Now I need to LIMIT comments for each post and so far I have used this query:
SELECT
`posts`.`id` AS `post_id`,
`posts`.`content` AS `post_content`,
`posts`.`parentid` AS `post_parentid`,
`posts`.`userid` AS `post_userid,
`comments`.`id`, 0 AS `comment_id`,
`comments`.`content` AS `comment_content`,
`comments`.`parentid` AS `comment_parentid`,
`comments`.`userid` AS `comment_userid,
IF( IFNULL( `comments`.`id`, 0 ) > 0, "comment", "post" ) AS `contenttype`
FROM `posts` AS `posts`
LEFT JOIN ( SELECT "" AS `hello` ) AS `useless` ON #pid := `posts`.`id`
LEFT JOIN ( SELECT
`posts`.`id` AS `id`,
`posts`.`id` AS `id`,
`posts`.`id` AS `id`,
`posts`.`id` AS `id`
FROM `posts`
WHERE `posts`.`parentid` = #pid
LIMIT 10
) AS `comments`ON `comments`.`parentid` = `posts`.`id`
WHERE
`posts`.`userid` = {USERID}
To archive this I have joined an useless "table" just to update #pid (parentid) variable.
Is this the only way to LIMIT subquery results? I don't like the idea of that useless JOIN.
What if I have to LIMIT posts in the example above without affecting the comments LIMIT. Can you please give me a better query?
The real reason of posting this question was to load 10 comments with 10 sub-comments for each comment. On the question I have asked to load posts & comments so the idea is the same.
The example posted in my question doesn't works because the subquery will executed before the variable #pid gets updated.
Because I'm using PHP then I'm posting here the solution in MySQL & PHP for this situation.
1 - First let's load posts with this SQL query
SELECT
`posts`.`id` AS `id`,
`posts`.`content` AS `content`,
`posts`.`parentid` AS `parentid`,
`posts`.`userid` AS `userid
FROM `posts` AS `posts`
WHERE
`posts`.`userid` = {USERID}
AND
`posts`.`parentid` = '0'
ORDER BY `posts`.`id` DESC
LIMIT 10
2 - Store posts information in $posts array:
$posts = [];
while ( $row = $result->fetch_object() )
{
$posts[] = (object) [ "id" => $row->id,
"content" => $row->content,
"userid" => $row->userid,
"comments" => []
];
}
3 - Prepare SQL to load comments:
$size = count( $posts );
$sql = "";
for ( $i = 0; $i < $size; $i ++ )
{
$sql .= ( $sql != "" ? "UNION ALL " : "" )
. "( "
. "SELECT "
. "`comments`.`id` AS `id`, "
. "`comments`.`content` AS `content`, "
. "`comments`.`parentid` AS `parentid`, "
. "`comments`.`userid` AS `userid "
. "FROM `posts` AS `comments` "
. "WHERE "
. "`comments`.`parentid` = '" . $post[ $i ]->id . "' "
. "ORDER BY `comments`.`id` ASC "
. "LIMIT 10 "
. ") ";
}
4 - After executing the $sql code let's store comments for each post:
while ( $row = $result->fetch_object() )
{
$posts[ $row->parentid ]->comments[] = (object)[
"id" => $row->id,
"content" => $row->content,
"userid" => $row->userid,
];
}
As you can see this can be used for also comments (instead of posts ) & sub-comments (instead of comments). MySQL variables are not helpful this time. Of course to create a pagination you have to add additional field (replies) in the table and update that during comment creation.
If someone has a better solution is welcomed.

how to get records in the following scenario

I have a table like below :
node_name id term_name
----------------------------------------------
test1 001 physics
test1 001 maths
test1 001 chemistry
test2 002 physics
test2 002 maths
Given a combination of term names I want to find all rows where the id set only contains exactly the given term names.
For example given the term names physics & maths my output should be like below
node_name id term_name
----------------------------------------------
test2 002 physics
test2 002 maths
Id set 001 contains also chemistry that is why it should not be included.
Your question: get all rows where no other rows with same id but other term_names exists
SELECT * FROM <table> x WHERE
term_name IN ('physics','maths') AND
NOT EXISTS (SELECT * FROM <table> WHERE id=x.id AND term_name NOT IN ('physics','maths'))
first of all you need to parse your query to convert the '&' to SQL 'OR' operator
in PHP :
//Parse the query
$arr = explode('&',$query);
$where = '';
//get the term count
$count = count($arr);
foreach($arr as $value){
$where .= "term_name = '" . $value . "' OR";
}
//Remove last or
$where = rtrim($where,'OR');
then :
use L
"select node_name ,count(1) as Total from my table where $where
group by node_name
having Total =" . $count
Finally :
your query must be in this format:
select x,count(1) as total from mytable where field1 = 'term1' or field1 = 'term2' having total = 2
One possible way to do this:
select id, node_name
from nodes join
(select id,
count(*)
from nodes
where node_name in ('physics','math')
group by id
having count(*) = 2 // this is set when generating the query ) as eligible_nodes
on nodes.id = eligible_nodes.id

MYSQL select based on the selects

You're help would be much appreciated...
If I have the following table and sample data... myGroupTable
group_Id : user_Id
1 : 3
1 : 7
1 : 100
2 : 3
2 : 7
2 : 100
2 : 104
4 : 42
4 : 98
4 : 13
I would like a sql statement that would...
Return a group_Id that has exactly the specified user_Id's in them.
eg... is there a group_Id that has User_Id's 3, 7 and 100
answer: group_id 1.
Please note that I dont want it to return a group_Id 2, as that also has a user_Id of 104 in it...
Kind regards J
SELECT
group_Id,
SUM(
IF user_Id = 3 THEN 1
ELSEIF user_Id = 7 THEN 2
ELSEIF user_Id = 100 THEN 4
ELSE 8
) AS bits
FROM myGroupTable
GROUP BY group_Id
HAVING bits=7
This assumes that you cannot have duplicate user_Ids for the same group_Id, eg this could never happens:
group user
1 3
1 3
Edit: You can build your query in the following way:
<?php
$ids = array(3, 7, 100);
$power = 2;
$query = "
SELECT
group_Id,
SUM(
IF user_Id = " .$ids[0]. " THEN 1 ";
foreach ($id in $ids) {
$query .= " ELSEIF user_Id = $id THEN " . $power;
$power = $power * 2;
}
$query .= " ELSE $power
) AS bits
FROM myGroupTable
GROUP BY group_Id
HAVING bits = " . ($power - 1);
Here's another alternative:
SELECT group_id, GROUP_CONCAT(user_id ORDER BY user_id) AS user_id_list
FROM group_user
GROUP BY group_id
HAVING user_id_list = '3,7,100'
another solution:
SELECT group FROM tbl WHERE group_id NOT IN (SELECT DISTINCT group_id FROM tbl WHERE user_id NOT IN(3,7,100)) AND user_id IN (3,7,100);
select group_id,
sum(
case user_id in(3,7,100)
when 1 then 1
else -99
end
) as must_be_3
from group_user
group by group_id
having must_be_3=3;

Does MySQL table row contain two values

I have a table with two columns:
ID1 | ID2
---------
1 | A
3 | V
1 | C
4 | B
5 | Q
1 | S
And I want to be able to find out if any row has, say ID1 = 5 and ID2 = Q , and return a true / false value.
Yes, Of course
SELECT * FROM table where ID1='5' and ID2='Q'
PHP (I am just guessing this backend)
$query = "SELECT * FROM table where ID1='5' and ID2='Q'";
$result = mysql_query($query);
if(mysql_num_rows($result)) { return true; }
else { return false; }
1 means TRUE for mysql
SELECT 1
FROM your_table
WHERE ID1 = 5 AND ID2 = 'Q'
for example this?
SELECT 1
FROM TABLE
WHERE ID1 = 5 AND ID2 = 'Q'
Efficient query for your purpose (faster than other examples):
SELECT 1 FROM table where ID1='5' and ID2='Q' LIMIT 1
PHP sample:
<?php
$query = "SELECT 1 FROM table where ID1='5' and ID2='Q' LIMIT 1";
echo bool(mysql_num_row(mysql_query($query)));
?>