I'm selecting a list of exam IDs from my database, and then using a random one to select from a list of questions. If there are no matching questions, I want to pick another random ID and try again. Here is the code I have now; it works but will not return any questions for some of the exams.
$query = $connect->prepare("SELECT * FROM exam WHERE level=? AND flag=?");
$query->bind_param("si",$level,$flag);
$query->execute();
$result = $query->get_result();
$resultCount= $result->num_rows;
if($resultCount>0)
{
while($row=$result->fetch_assoc())
{
$list[]=$row['id'];
}
$indexRand=array_rand($list, 1);
$query->close();
}
$query2 = $connect->prepare("SELECT * FROM questions WHERE exam_id=?");
$query2->bind_param('i', $list[$indexRand]);
$query2->execute();
$question_result = $query2->get_result();
$resultCount2= $question_result->num_rows;
if($resultCount2>0)
{
while($questions_rows=$question_result->fetch_assoc())
{
$list2[]=$questions_rows;
}
}
If I understand correctly, you can do this in a single database query.
SELECT q.*
FROM questions q
LEFT JOIN (
SELECT e.id
FROM exams e
LEFT JOIN questions q ON (e.id = q.exam_id)
WHERE level = ? AND flag = ? AND q.id IS NOT NULL
ORDER BY RAND()
LIMIT 1
) i ON (q.exam_id = i.id)
WHERE i.id = q.exam_id
You join in a subquery as a table in your query. The subquery selects a single random item from the exams table, and itself joins in the questions table, to make sure that some questions exist. Demo is here: http://sqlfiddle.com/#!9/b394af/1
Related
I have 2 tables - users and articles.
users:
user_id (int)
name (varchar)
articles:
article_id (int)
user_id (int)
title (varchar)
description (text)
In my application I need to display 20 RANDOM articles on a page.
My query is like this:
SELECT a.title
, a.description
, u.name
FROM articles a
JOIN users u
USING (user_id)
ORDER
BY RAND()
LIMIT 20
A user can have any number of articles in the database.
Now the problem is sometimes out of 20 results, there are like 9-10 articles from one single user.
I want those 20 records on the page to not contain more than 3 (or say 4) articles from a particular user.
Can I achieve this through SQL query. I am using PHP and MySQL.
Thanks for your help.
You could try this?
SELECT * FROM
(
SELECT B.* FROM
(
SELECT A.*, ROW_NUMBER() OVER (PARTITION BY A.USER_ID ORDER BY A.R) USER_ROW_NUMBER
FROM
(
SELECT a.title, a.description, u.name, RND() r FROM articles a
INNER JOIN users u USING (user_id)
) A
) B
WHERE B.USER_ROW_NUMBER<=4
) C
ORDER BY RAND() LIMIT 20
Mmm, intresting I don't think this is possible through a pure sql query.
My best idea would be to have an array of the articles that you'll eventually display query the database and use the standard SELECT * FROM Articles ORDER BY RAND() LIMIT 20
The go through them, making sure that you have indeed got 20 articles and no one has breached the rules of 3/4 per user.
Have another array of users to exclude, perhaps using their user id as an index and value of a count.
As you go through add them to your final array, if you find any user that hits you rule add them to the array.
Keep running the random query, excluding users and articles until you hit your desired amount.
Let me try some code (it's been a while since I did php)
$finalArray = [];
$userArray = [];
while(count($finalArray) < 20) {
$query = "SELECT * FROM Articles ";
if(count($finalArray) > 0) {
$query = $query . " WHERE articleID NOT IN(".$finalArray.")";
$query = $query . " AND userID NOT IN (".$userArray.filter(>4).")";
}
$query = $query . " ORDER BY Rand()";
$result = mysql_query($query);
foreach($row = mysql_fetch_array($result)) {
if(in_array($finalArray,$row) == false) {
$finalArray[] = $row;
}
if(in_array($userArray,$row[userId]) == false) {
$userArray[$row[userId]] = 1;
}
else {
$userArray[$row[userId]] = $userArray[$row[userId]] + 1;
}
}
I first search all questions info. from "question" table including title, content, user etc.
the Code:
$sql = "select * FROM question where id>0 ORDER BY id ASC";
$result1 = mysql_query($sql);
$res=Array();
And then I want to search the user's point from "user" table. So I must search point for each user in each row from the result1
The Code:
while($rows=mysql_fetch_assoc($result1))
{
$res[]=$rows;
$user = $rows['user'];
$sql2 = "select point from user where name='$user'";
$result2 = mysql_query($sql2);
}
My problem is how to combine all the users' point(result2) with the questions info.(result1) together so that I can return a json for each row.
Use left join, as my understanding this work for you
$sql = "SELECT q.*, u.point AS point FROM question AS q LEFT JOIN user AS u ON q.user = u.name WHERE q.id > 0 ORDER BY q.id ASC";
$result = mysql_query($sql);
It's better go with the joins here i am giving you the query.i hope it may helps you
select * from question q,user u where q.id>0 ORDER BY id ASC
try something like this:using left join
select question.*,user.point FROM question left join user on user.name= question.name where id>0 ORDER BY id ASC
i'm in the process of joining two tables together under two different conditions. For primary example, lets say I have the following nested query:
$Query = $DB->prepare("SELECT ID, Name FROM modifications
WHERE TYPE =1 & WFAbility = '0'");
$Query->execute();
$Query->bind_result($Mod_ID,$Mod_Name);
and this query:
$Query= $DB->prepare("SELECT `ModID` from `wfabilities` WHERE `WFID`=?");
$Query->bind_param();
$Query->execute();
$Query->bind_result();
while ($Query->fetch()){ }
Basically, I want to select all the elements where type is equal to one and Ability is equal to 0, this is to be selected from the modifications table.
I further need to select all the IDs from wfabilities, but transform them into the names located in modifications where WFID is equal to the results from another query.
Here is my current semi-working code.
$Get_ID = $DB->prepare("SELECT ID FROM warframes WHERE Name=?");
$Get_ID->bind_param('s',$_GET['Frame']);
$Get_ID->execute();
$Get_ID->bind_result($FrameID);
$Get_ID->fetch();
$Get_ID->close();
echo $FrameID;
$WF_Abilties = $DB->prepare("SELECT ModID FROM `wfabilities` WHERE WFID=?");
$WF_Abilties->bind_param('i',$FrameID);
$WF_Abilties->execute();
$WF_Abilties->bind_result($ModID);
$Mod_IDArr = array();
while ($WF_Abilties->fetch()){
$Mod_IDArr[] = $ModID;
}
print_r($Mod_IDArr);
$Ability_Name = array();
foreach ($Mod_IDArr AS $AbilityMods){
$WF_AbName = $DB->prepare("SELECT `Name` FROM `modifications` WHERE ID=?");
$WF_AbName->bind_param('i',$AbilityMods);
$WF_AbName->execute();
$WF_AbName->bind_result($Mod_Name);
$WF_AbName->fetch();
$Ability_Name[] = $Mod_Name;
}
print_r($Ability_Name);
See below:
SELECT ModID,
ID,
Name
FROM modifications M
LEFT JOIN wfabilities WF
ON WF.ModID = M.ID
WHERE TYPE =1 & WFAbility = '0'
To do this, you need to join your tables, I'm not quite sure what you are trying to do so you might have to give me more info, but here is my guess.
SELECT ID, Name, ModID
FROM modifications
JOIN wfabilities
ON WFID = ID
WHERE TYPE = '1'
AND WFAbility = '0'
In this version I am connecting the tables when WFID is equal if ID. You will have to tell me exactly what is supposed to be hooking to what in your requirements.
To learn more about joins and what they do, check this page out: MySQL Join
Edit:
After looking at your larger structure, I can see that you can do this:
SELECT modifications.Name FROM modifications
JOIN wfabilities on wfabilities.ModID = modifications.ID
JOIN warframes on warframes.ID = wfabilities.WFID
WHERE warframes.Name = 'the name you want'
This query will get you an array of the ability_names from the warframes name.
This is the query:
"SELECT A.ID, A.Name,B.ModID,C.Name
FROM modifications as A
LEFT JOIN wfabilities as B ON A.ID = B.WFID
LEFT JOIN warframes as C ON C.ID = B.WFID
WHERE A.TYPE =1 AND A.WFAbility = '0' AND C.Name = ?"
Im trying to get in Array that contains the results from a MYSQL query.
I have 2 ids stored in the table hitlist user_id and mark_id
they need to join in the table users to retrieve there usernames that match there id's and in the future other variables.
i have this working in a weird way and was hopeing to get this working in a more efficent simple way similar to this
$Hitlists = $db->query("SELECT * FROM hitlist JOIN users ON hitlist.user_id = users.id AND hitlist.mark_id = users.id")->fetchAll();
This is the code i have that is working...for now it looks like it might give me problems later on.
<?php
$index = 0;
$Hitlists = array();
$st = $db->query("SELECT * FROM hitlist JOIN users ON hitlist.user_id = users.id")->fetchAll();
$sth = $db->query("SELECT * FROM hitlist JOIN users ON hitlist.mark_id = users.id")->fetchAll();
foreach($st as $id)
{
$Hitlists[] = $id;
}
foreach($sth as $id)
{
$Hitlists[$index]['markedby'] = $id['username'];
$Hitlists[$index]['mark_id'] = $id['mark_id'];
$index++;
}
The way you are joining the table is wrong. You can get the exact records you want, you need to join users table twice to get the username of each ID
SELECT a.*,
b.username User_name,
c.username mark_name
FROM hitlist a
INNER JOIN users b
ON a.user_id = b.id
INNER JOIN users c
ON a.mark_id = c.id
and you can access
$result['User_name']
$result['mark_name']
i am having trouble creating a single mysql query for what i am trying to do here.
first off, i will show you the table structures and fields of the tables i am using for this particular query:
users:
- id
- name
- photo_name
- photo_ext
user_attacks:
- id
- level
user_news_feed:
- id
- id_user
- id_status
- id_attack
- id_profile
- id_wall
- the_date
user_status:
- id
- status
user_wall:
- id
- id_user
- id_poster
- post
whenever the user posts an attack, or status update, updates their profile, or posts on someones wall, it inserts the relevant data into its respective table and also inserts a new row into the user_news_feed table.
now, what i want to do is select the last 10 news feed items from the database. these news feed items need to grab relevant data from other tables as long as their value is not 0. so if the news feed is for a status update, the id_status would be the id of the status update in the user_status table, and the "status" would be the data needing to be selected via a left join. hope that makes sense.
heres my first mysql query:
$sql = mysql_query("select n.id_user, n.id_status, n.id_attack, n.id_profile, n.id_wall, n.the_date, u.id, u.name, u.photo_name, u.photo_ext, s.status
from `user_news_feed` as n
left join `users` u on (u.id = n.id_user)
left join `user_status` s on (s.id = n.id_status)
where n.id_user='".$_GET['id']."'
order by n.id desc
limit 10
");
now this works great, except for 1 problem. as you can see the user_wall table contains the id's for 2 different users. id_user is the user id the post is being made for, and id_poster is the user id of the person making that wall post. if the user makes a wall post on his/her own wall, it is inserted into the database as a status update into the user_status table instead.
so i have a conditional statement within the while loop for the first query, which has another sql query within it. here is the whole code for the while loop and second sql query:
while ($row = mysql_fetch_assoc($sql))
{
if ($row['id_wall'] != 0)
{
$sql_u = mysql_query("select u.id, u.name, u.photo_name, u.photo_ext, w.post
from `user_wall` as w
left join `users` u on (u.id = w.id_poster)
where w.id='".$row['id_wall']."'
");
while ($row_u = mysql_fetch_assoc($sql_u))
{
$row['photo_name'] = $row_u['photo_name'];
$row['photo_ext'] = $row_u['photo_ext'];
$row['id_user'] = $row_u['id'];
$row['name'] = $row_u['name'];
$content = $row_u['post'];
}
}
else
{
if ($row['id_status'] != 0)
$content = $row['status'];
else if ($row['id_attack'] != 0)
$content = '<i>Had an attack</i>';
else if ($row['id_profile'] != 0)
$content = '<i>Updated profile</i>';
}
echo '<li'.(($count == $total_count) ? ' class="last"' : '').'>';
echo '<img src="images/profile/'.$row['photo_name'].'_thumb.'.$row['photo_ext'].'" alt="" />';
echo '<div class="content">';
echo '<b>'.$row['name'].'</b>';
echo '<span>'.$content.'</span>';
echo '<small>'.date('F j, Y \a\t g:ia', $row['the_date']).'</small>';
echo '</div>';
echo '<div style="clear: both;"></div>';
echo '</li>';
}
i hope what i am trying to do here makes sense. so basically i want to have both sql queries ($sql, and $sql_u) combined into a single query so i do not have to query the database every single time when the user_news_feed item is a wall post.
any help would be greatly appreciated and i apologise if this is confusing.
SELECT n.id_user, n.id_status, n.id_attack, n.id_profile, n.id_wall, n.the_date,
u.id, u.name, u.photo_name, u.photo_ext, s.status,
w.id AS wall_user_id, w.name AS wall_user_name,
w.photo_name AS wall_user_photo_name,
w.photo_ext AS wall_user_photo_ext,
w.post
FROM user_news_feed AS n
LEFT JOIN users AS u ON (u.id = n.id_user)
LEFT JOIN user_status s ON (s.id = n.id_status)
LEFT JOIN (SELECT a.id AS id_wall, b.id, b.name, b.photo_name, b.photo_ext, a.post
FROM user_wall AS a
LEFT JOIN users AS b ON (b.id = a.id_poster)
) AS w ON w.id_wall = n.id_wall
WHERE n.id_user = ?
ORDER BY n.id desc
LIMIT 10
The '?' is a placeholder where you can provide the value of $_GET['id'].
Basically, this adds an extra outer join, to the main query (and some extra columns, which will be NULL if the news feed event is not a wall posting), but the outer join is itself the result of an outer join.
Back again ;)
Anyway, forget about merging the queries in my opinion.
What you should do instead is to do the first query, loop through all the results and store all "id_wall"s in a separate array... then rather than doing a separate query per "id_wall" you do this:
$wallids = array();
while ($row = mysql_fetch_assoc($sql))
{
$wallids[] = $row['id_wall'];
// also store the row wherever you want
}
$sql_u = mysql_query("select u.id, u.name, u.photo_name, u.photo_ext, w.post
from `user_wall` as w
left join `users` u on (u.id = w.id_poster)
where w.id IN ('".implode(',', $wallids) ."')
");
$wallids being an array with all the "id_wall"s. So now you have a total of 2 queries.