Complicated MySQL Query? - php

I'm working on a 'social sharing' kind of thing for music, and I just ran in to a problem. My friends table is structured like this:
| id | f1 | f2 | status |
----------------------------------------------
| 000001 | username1 |username2| 0 |
| 000002 | username4 |username7| 1 |
My rates table is structured like this:
| id | username | songname | songid | plus | minus |
----------------------------------------------------
|0001| username1| Songname | 000001 | 0001 | 00000 |
|0002| username3| Song2222 | 000002 | 0000 | 00001 |
And so what i'm trying to do, is to get a random list of 3 friends where the status is 1, and then query the rates table for each random friend's most recent rate where plus = 1. 'Recency' (if you will) is based on the ID which is auto incremental.
That part isn't the really tough bit, lol. The tough bit, is that the user could be in EITHER f1 OR f2, so the query needs to contain an OR, and if the user is in f1, it'll need to get the corresponding f2, and vice versa.
Then check if they are friends and status=1, then query rates for the most recent rates by the 3 random friends. Then download those data bits, then write 3 strings similar to the example below
username 3 +1'd Song2222
If anyone would know how to write a script/query like this in PHP/MySQL, I'd be really grateful! Haha
Thanks! :)
Edit 3*
While waiting for responses, I worked out this code, which partially works!
<? session_start();
$user = $_SESSION['username'];
mysql_connect("localhost", "xxxxx", "xxxx") or die(mysql_error());
mysql_select_db("xxxxxx") or die(mysql_error());
$q1data = mysql_query("SELECT * FROM friends WHERE (f1='$user') OR (f2='$user') AND status=1 ORDER BY RAND() LIMIT 1") ;
if(mysql_num_rows($q1data)===1)
{
$q1result = mysql_fetch_assoc($q1data);
$q1f1 = $q1result['f1'];
$q1f2 = $q1result['f2'];
if($q1f1==$user) {
$oq2un1 = $q1f2;
}
if($q1f2==$user) {
$oq2un1 = $q1f1;
}
}
$q2data = mysql_query("SELECT * FROM friends WHERE (f1='$user') OR (f2='$user') AND status=1 ORDER BY RAND() LIMIT 1") ;
if(mysql_num_rows($q2data)===1)
{
$q2result = mysql_fetch_assoc($q2data);
$q2f1 = $q2result['f1'];
$q2f2 = $q2result['f2'];
if($q2f1==$user) {
$oq2un2 = $q2f2;
}
if($q2f2==$user) {
$oq2un2 = $q2f1;
}
}
$q3data = mysql_query("SELECT * FROM friends WHERE (f1='$user') OR (f2='$user') AND status=1 ORDER BY RAND() LIMIT 1") ;
if(mysql_num_rows($q3data)===1)
{
$q3result = mysql_fetch_assoc($q3data);
$q3f1 = $q3result['f1'];
$q3f2 = $q3result['f2'];
if($q3f1==$user) {
$oq2un3 = $q3f2;
}
if($q3f2==$user) {
$oq2un3 = $q3f1;
}
}
/************************************* SECOND SET OF QUERIES ******************************************/
$q4data = mysql_query("SELECT * FROM rates WHERE username='$oq2un1' AND plus=1 ORDER BY id LIMIT 1");
if(mysql_num_rows($q4data)===1)
{
$q4result = mysql_fetch_assoc($q4data);
$finalusername1 = $q4result['username'];
$q4songid = $q4result['song_id'];
$q4songname = $q4result['songname'];
}
$q5data = mysql_query("SELECT * FROM rates WHERE username='$oq2un2' AND plus=1 ORDER BY id LIMIT 1");
if(mysql_num_rows($q5data)===1)
{
$q5result = mysql_fetch_assoc($q5data);
$finalusername2 = $q5result['username'];
$q5songid = $q5result['song_id'];
$q5songname = $q5result['songname'];
}
$q6data = mysql_query("SELECT * FROM rates WHERE username='$oq2un3' AND plus=1 ORDER BY id LIMIT 1");
if(mysql_num_rows($q6data)===1)
{
$q3result = mysql_fetch_assoc($q6data);
$finalusername3= $q6result['username'];
$q6songid = $q6result['song_id'];
$q6songname = $q6result['songname'];
}
$socialmuze_string1 = $finalusername1." recently <font color='#00FF00'>+1'd</font> <a href='song?id=".$q4songid."'>".$q4songname."</a><br>";
$socialmuze_string2 = $finalusername2." recently <font color='#00FF00'>+1'd</font> <a href='song?id=".$q5songid."'>".$q5songname."</a><br>";
$socialmuze_string3 = $finalusername3." recently <font color='#00FF00'>+1'd</font> <a href='song?id=".$q6songid."'>".$q6songname."</a><br>";
echo $finalusername1." ".$q4songname."<br>";
echo $finalusername2." ".$q5songname."<br>";
echo $finalusername3." ".$q6songname."<br>";
?>

I think it might help modifying the friends query such that it only returns the friends. Picking up portions from your code example, this is how I'd suggest doing it:
session_start();
$user = $_SESSION['username'];
// retrieve random 3 friends
$rsFriends = mysql_query('SELECT `id`,
(CASE
WHEN `f1` = \'' . mysql_real_escape_string($user) . '\' THEN `f2`
WHEN `f2` = \'' . mysql_real_escape_string($user) . '\' THEN `f1`
END) AS `friend`
FROM `friends`
WHERE `status` = 1
AND (`f1` = \'' . mysql_real_escape_string($user) . '\'
OR `f2` = \'' . mysql_real_escape_string($user) . '\')
ORDER BY RAND()
LIMIT 3');
while ($row = mysql_fetch_assoc($rsFriends)) {
// retrieve the most recent rate entry where plus = 1
$rsRates = mysql_query('SELECT `id`, `username`, `songname`, `songid`, `plus`, `minus`
FROM `rates`
WHERE `username` = \'' . mysql_real_escape_string($row['friend']) . '\'
AND `plus` = 1
ORDER BY `id` DESC
LIMIT 1');
while ($row1 = mysql_fetch_assoc($rsRates)) {
// $row1 is the array that contains the most recent rate where plus = 1
}
}
The benefit with modifying the friends query is that it will always give you the name of the friend in a single column. There are other ways to write the friends query, like using UNION may be, but I think this one is simple and should work as nicely.
I haven't tested the code above, so please feel free to fix it if in case I've used wrong table or column names. Also, though the above code sample is keeping in lines with your example, you might as well do the above in a single query using JOINs.
Hope this helps!

Let's work on the SQL part first.
In your comment, you said you are using
SELECT * FROM friends WHERE (f1='$user') OR (f2='$user') LIMIT 3
You also said you wanted to get a random sample. MySQL has the construct ORDER BY RAND() so that your first 3 will be randomized.
Let's figure this out from the inside out.
If you issued this Select statement:
SELECT * FROM friends where status=1 and (f1='$user' OR f2='$user') order by RAND() limit 3
Then I think you would get 3 random rows from the friends table where the status=1.
(This would be much easier if friends were normalized and f1 and f2 were a single column with an additional column indicating how f1 and f2 were different. As it stands, there is a computationally expensive way to get these two columns into one, assuming they are the same type, say varchar(13).)
SELECT f1 FROM friends where status=1 and f1='$user' order by RAND() limit 3
union all
SELECT f2 FROM friends where status=1 and f2='$user' order by RAND() limit 3
That will get you 6 usernames that are called $user.
Working your way out, you will need a query to surround the first, making a subselect statement. To do the next step, you might consider:
SELECT * from rates where username in (
SELECT f1 FROM friends where status=1 and f1='$user' order by RAND() limit 3
union all
SELECT f2 FROM friends where status=1 and f2='$user' order by RAND() limit 3
)
Again, this would not be quite so complex if you were to normalize the friends table.

Related

Dynamically selecting tables in mySQL

I have a query in mySQL
SELECT id FROM admin_products;
which return a list of ids, like so
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
+------+
And I was using PHP to dynamically generate tables like
vendor_1, vendor_2, vendor_3, vendor_4, vendor_5
Now I want to write a query to retrieve the price and quantity from the table id
For example
"ENTER QUERY HERE"
Should retrieve
+-----------------------------+
| id | price | quantity |
+-----------------------------+
| 1 | 23| 13| // price and quantity retrieved from table vendor_1 since id=1
| 2 | 158| 85| // price and quantity retrieved from table vendor_2 since id=2
| 3 | 15| 7| // price and quantity retrieved from table vendor_3 since id=3
| 4 | 112| 9| // price and quantity retrieved from table vendor_4 since id=4
| 5 | 123| 199| // price and quantity retrieved from table vendor_5 since id=5
+-----------------------------+
What I'm doing now in PHP is:
$conn = mysqli_connect($server,$user,$pwd,$db);
$sql = "SELECT id FROM admin_products";
$res = mysqli_query($conn,$sql);
if(mysqli_num_rows($res)>0){
while($row = mysqli_fetch_assoc($res)){
$product = array();
$innerSQL = "SELECT price,quantity FROM vendor_".$row['id'];
$innerRes = mysqli_query($conn,$innerSQL);
if(mysqli_num_rows($innerRes)>0){
while($innerRow = mysqli_fetch_assoc($innerRes)){
array_push($product,$row['id']);
array_push($product,$innerRow['price']);
array_push($product,$innerRow['quantity']);
}
}
}
}
But it takes two hits to the mySQL database. Can't it be reduced to one?
EDIT
I have later on realized that my database structure was incorrect and dynamically creating tables is a very bad idea and could spell disaster later on
-Solution 1:
Note: This will only work if you have in your vendor_x tables id for the vendor id to match them with. (As Strawberry said, this is a terrible idea to dynamically generate tables).
After selecting the correct id you can do something like this:
connect to the MySql Server
Then you can create the table name and store it in a variable.
$tableName = 'vendor_' . $id;
I would suggest after that to have a check if the table exists with a simple query:
$sql = "SHOW TABLES LIKE '$tableName'";
If this returns empty result you can throw an exception that the table does not exist or handle it whatsoever way you would like.
After checking every table, to be sure it exists, you can create your query.
$joins = "";
$sql = "
SELECT
v.id,
price,
quantity
FROM
vendors AS v
";
foreach ($ids as $id) {
$tableName = "vendor_" . $id;
$tableAlias = "v".$id;
$joins .= " LEFT JOIN " . $tableName . " AS ". $tableAlias ."
ON (v.id = ". $tableAlias .".vendor_id) ";
}
$sql .= $joins;
Then execute the query.
-Solution 2:
Create only one table to manage your vendors. It should have a structure like this :
`id` // AI value
`vendor_id` // The id of the vendor to easily join it afterwards
`price`
`quantity`
You can name it something like vendor_product or whatsoever
And now you have only one simple query:
$sql = "
SELECT
v.id,
vp.quantity,
vp.price
FROM
vendors AS v
LEFT JOIN vendor_product AS vp
ON (vp.vendor_id = v.id)
";
EDIT for the comment about the structure:
You will need one table for the vendors, such so:
`vendor`:
`id`, //AI value
`username`,
`password` // I suggest to you not to keep it in plain text.
`vendor_product` :
`id`, //AI value
`vendor_id`,
`price`,
`quantity`
I don't know here if you are going to store more information about each product, but this should do the trick.
How to show the product with least price ?
You need to match them by somehow and group by that selecting minimum price.
Try this if it suits
$table = "vendor"."_".$id; // this will create table name if $id = 1 then $table = vendor_1;
mysqli_query($connect , "SELECT * FROM $table");
2nd
If you want to fetch data of all table at once then
1) fetch id from admin_products and store in an array like
$ids = array(1,2,3,4,5);
2) Now loop throw array and create sql;
$sql = "SELECT * FROM ";
$ids = array(1,2,3,4,5);
foreach($ids as $id){
$table = "vendor"."_".$id; // this will create table name if $id = 1 then $table = vendor_1;
$sql .=" $table,";
}
$sql = rtrim($sql,",");// this will trim the last comma
echo $sql;
// output SELECT * FROM vendor_1, vendor_2, vendor_3, vendor_4, vendor_5

Selecting max percentage from the column for every subject

I have table called skills in which different skills are stored for that particular user in % so basically i want to select highest skills for that user from skills table and want to echo it in while loop for every user
user | skills |
userID | PHP |
userName | C++ |
<?php
$sql= "(SELECT *,max(skills) as higherPercentage
FROM skill LEFT JOIN user ON skill.userID=user.userID ORDER BY signup_date DESC ) ";
$res = mysql_query($sql) or die(mysql_error());
while ($row = mysql_fetch_assoc($res)) {
?>
So If the user has Highest vale 99 compare to all the skills then it should be selected for him
Store values in like 1,0.5,6 not like 100%,10%
and then use
SELECT MAX(column_name) FROM table_name group by (user)

I want to return data form a table depending on whats in another table?

I have a table passenger_table and a table users!
passenger_table
user_id j_id
1 1
34 1
54 1
users
user_id Firstname Lastname
1 Patrick Connell
34 John Murphy
54 Connell Jones
I know this might be straight forward enough but I just want to loop out all the users firstnames and lastnames where journey id is = 1.
So the output would be the 3 users mentioned above first and lastnames.
This is what I got so far.....
//Get passenger id
$id = $_GET['id'];
$resultp = mysql_query("SELECT * FROM passenger_journey WHERE j_id=$id")
or die(mysql_error());
$rowp = mysql_fetch_array($resultp);
$passenger_id = $rowp['user_id'];
//Get Passenger Details
$resultpd = mysql_query("SELECT * FROM users WHERE user_id=$passenger_id")
or die(mysql_error());
while($rowpd = mysql_fetch_array($resultpd)) {
echo '' . $rowpd['firstname'] . ' ' . $rowpd['lastname'] . '<br/>';
}
At the moment I can only retrieve one record.
You should join the table, on the associative ID. Read more about table joins here: http://dev.mysql.com/doc/refman/5.0/en/join.html
SELECT u.user_id, u.firstname, u.lastname, p.j_id
FROM users u
LEFT JOIN passenger_table p ON p.user_id=u.user_id
WHERE p.j_id=1;

Checking users options from default

I have table:
user_id | song_id| points
--------|----------------
2 | 1 | 0
2 | 2 | 1
2 | 3 | 2
2 | 4 | 3
2 | 5 | 4
And I need to check if the user have changed the points value.
Therefore it should be something like:
while ($row = mysql_fetch_array($query)) {
$userID = $row['user_id'];
$songID = $row['song_id'];
$points = $row['points'];
if($songID-$points==1){
echo $userID."<br>";
}
But this will print out every occasion of userID where the song-id - points=1.
I need to print out only these user_id's that have all the values =1 and the username must echo'd only once.
EDIT:
SELECT DISTINCT user_id WHERE (song_id - points) = 1
This is half way there. This echo's user_ids' where the song_id - points = 1, but if the user is reordered (i use jQuery sortable) the list, then there can be some rows that is "song_id - points = 1".
My script must echo only these user_id-s, where users every song_id - points = 1, not only one
SELECT DISTINCT user_id FROM table WHERE (song_id - points) = 1
After edit:
SELECT table.user_id
FROM table
LEFT JOIN (
SELECT user_id, COUNT(*) AS C FROM table) AS T2
ON table.user_id = T2.user_id
WHERE (table.song_id - table.points) = 1
GROUP BY table.user_id
HAVING COUNT(*) = T2.C
You can first filter the users which has modified point values:
SELECT DISTINCT user_id FROM table
WHERE (song_id - points) != 1
Then you can use fetch the users which doesn't fit the above condition:
SELECT DISTINCT user_id FROM table
WHERE user_id NOT IN (
SELECT DISTINCT user_id FROM table
WHERE (song_id - points) != 1
)
According to your last edit this last SQL statement might work.
You can check a working example.
Here is what you're looking for:
select user_id from (
select user_id, if(song_id - points = 1, 0, 1) flag from t
) as S
group by user_id
having sum(flag) = 0
And here is a working example.
In case I didn't understand the requirements this shows all users who don't even have one row in which song_id - points != 1, i.e, all users who have all rows that match song_id - points = 1
Or maybe, if you prefer a different approach that might be more efficient:
select distinct t1.user_id from t t1
where not exists (
select * from t t2
where t2.song_id - t2.points != 1 and t1.user_id = t2.user_id
)
Here is the working example.
Not sure I understand the why of the situation, but a simple control-break structure will achieve the desired result ...
$old_id = '';
$good = false;
while($row = mysql_fetch_array($query)){
//check to see if we have a new user ...
if($row['user_id'] != $old_id){
//check to see if all values were == 1
if($good){
echo $old_id . '<br />';
}
//re-initialize variables
$good = true;
$old_id = $row['user_id'];
}
//if value != 1, we won't print the user ...
if($row['song_id'] - $row['points'] != 1){
$good = false;
}
}
//final end-of-loop condition ...
if($good){
echo $old_id . '<br />';
}
OK, here's a query that's a lot more simple than the join above:
SELECT user_id, sum(song_id) as song_total, sum(points) as point_total, count(*) AS cnt FROM table GROUP BY user_id
If the difference between song_id and points is 1 for every song, then the difference between the totals will equal the number of rows for that user ... so using this query:
while($row = mysql_fetch_array($query)){
if($row['cnt'] == ($row['song_total'] - $row['point_total'])){
echo $row['user_id'] . '<br />';
}
}

Combine two word from a MySQL database at random

I have a database with nouns and adjectives for example:
id | type | word
-----------------------
1 | noun | apple
2 | noun | ball
3 | adj | clammy
4 | noun | keyboard
5 | adj | bloody
ect...
I want to create one query what will grab 10 random adjectives and 10 random nouns and put them together.
Having trouble doing it, is this possible?
Thank you!
You can get 10 random elements per type with queries like this:
select word from YOUR_TABLE where type = 'noun' order by rand() limit 10;
select word from YOUR_TABLE where type = 'adj' order by rand() limit 10;
and then put them together in your PHP code, like so:
$phrases = array();
$adj_result = mysql_query("SELECT word FROM words WHERE type = 'adj' ORDER BY RAND() LIMIT 10");
$noun_result = mysql_query("SELECT word FROM words WHERE type = 'noun' ORDER BY RAND() LIMIT 10");
while($adj_row = mysql_fetch_assoc($adj_result)) {
$noun_row = mysql_fetch_assoc($noun_result);
$phrases[$adj_row['word']] = $noun_row['word'];
}
print_r($phrases);
Please note that this code is not very safe (it makes the assumption that the second query always yields as least as many results as the first), but you get the idea.
Edit: Here's a single SQL query that should do it:
select t1.word, t2.word
from
((select word from YOURTABLE where type = 'adj' order by rand()) as t1),
((select word from YOURTABLE where type = 'noun' order by rand()) as t2)
order by rand()
limit 10;
EDIT: removed example

Categories