PHP and MySQL problems - php

I'm trying to count how many times a certain article has been graded for example how many times have users_articles_id 3 been graded by my members.
I'm also trying to count the points for a certain article for example users_articles_id 3 is related to the ratings database by its ratings_id the rating points should be a total of 13.
I was wonder if I was doing this right because to me it looks all wrong? I was hoping if some one can help me fix this? And where should my code go exactly?
I'm using PHP and MySQL?
Here is my MySQL tables
CREATE TABLE articles_grades (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
ratings_id INT UNSIGNED NOT NULL,
users_articles_id INT UNSIGNED NOT NULL,
user_id INT UNSIGNED NOT NULL,
date_created DATETIME NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE ratings (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
points FLOAT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);
Database Input
articles_ratings
id ratings_id users_articles_id user_id date_created
1 3 2 32 2010-01-13 02:22:51
2 1 3 3 2010-01-13 02:23:58
3 2 3 45 2010-01-13 02:24:45
ratings
id points
1 10
2 3
3 5
Here is the PHP code I'm trying to fix.
// function to retrieve rating
function getRating(){
$sql1 = "SELECT COUNT(*)
FROM articles_ratings
WHERE users_articles_id = '$page'";
$result = mysql_query($sql1);
$total_ratings = mysql_fetch_array($result);
$sql2 = "SELECT COUNT(*)
FROM ratings
JOIN ratings ON ratings.id = articles_ratings.ratings_id
WHERE articles_ratings.users_articles_id = '$page'";
$result = mysql_query($sql2);
$total_rating_points = mysql_fetch_array($result);
if(!empty($total_rating_points) && !empty($total_ratings)){
// set the width of star for the star rating
$rating = (round($total_rating_points / $total_ratings,1)) * 10;
echo $rating;
} else {
$rating = 100;
echo $rating;
}
}

Well I think there are several issues here.
1) You are defining a table called articles_grades, not articles_ratings, as in your code.
2) Why do articles_grades and ratings need to be in separate tables? There is a one-to-one correspondence between those tables.
3) You need to do sum(points) in your second query.
4) You can combine both queries into a single query.
Here's how I would do it if you don't change the schema:
<?php
mysql_connect('localhost','root','fake123123');
mysql_select_db('test');
$result = mysql_query('SELECT users_articles_id,count(*),sum(r.points)
FROM articles_grades ag,ratings r
WHERE ag.ratings_id = r.id
GROUP BY users_articles_id');
if (!$result)
die('invalid');
else{
echo '<table><tr><th>Article Id</th><th>#Ratings</th><th>#Points</th></tr>';
$a = mysql_fetch_row($result);
while($a){
echo '<tr><td>'.implode('</td><td>',$a).'</td></tr>';
$a = mysql_fetch_row($result);
}
echo '</table>';
}
?>
You can run this as a CGI script. It should return a table of the results.
Let me know if this helps.

Related

MySql Query - optimization with varchars, indexs, taking over an hour to run

So I need to run a query that I do not know the UUID - but need to find it... so I am using the street num, street name, and a company UUID to find it
I have a few million records, and this took query is taking around an HOUR!!
any advice to speed it up?
gisPoints
UUID Indexed Unique varchar(36)
street_num int(11)
street_name varchar(128)
geoPoint_temp
UUID Indexed Unique varchar(36)
street_num int(11)
street_name varchar(128)
gcomUUID Indexed varchar(36)
update geoPoint_temp as temp JOIN gisPoints as `prod` on prod.gcomUUID=temp.gcomUUIDand prod.street_num=temp.street_num and prod.street_name REGEXP(temp.street_name)
set temp.UUID=prod.UUID,temp.customerUUID=prod.customerUUID WHERE temp.`uploadstate` = '1'";
Assuming you have the following values (in PHP):
$street_num = ...;//something
$street_name = ...;//something
$gcomUUID = ...;//something
If you run the following sql code:
$sql = "SELECT * FROM (
SELECT * FROM (
SELECT * FROM geoPoint_temp WHERE gcomUUID = $gcomUUID)
WHERE street_name = $street_name)
WHERE street_num = $street_num;"
You should obtain a list of rows (0 or more) from geoPoint_temp that have matching values, and it should be relatively fast even in a big table.
After obtaining those rows, you can check if the row count is greater than zero, and if so update the rows. If your using MySQL (PDO), you could do something similar to the following:
$count = $stmt->rowCount();
if ($count>0)
{
$rows = $stmt->fetchAll();
foreach ($rows as $row)
{
$sql = "UPDATE geoPoint_temp SET ... WHERE UUID = ".$row['UUID'];
$stmt = $conn->prepare($sql);
$stmt->execute();
}
}
Let me know if that helped.
EDITED:
Try the following as well and let me know if it works:
$sql = "
UPDATE geoPoint_temp SET ... WHERE UUID IN
(SELECT * FROM (
SELECT * FROM (
SELECT * FROM geoPoint_temp WHERE gcomUUID = $gcomUUID)
WHERE street_name = $street_name)
WHERE street_num = $street_num);"
And replace ... with the values you want updated.
This runs in 1.5 seconds opposed to the hours it was taking before
Much help to #Webeng for pointing us in the right direction!
$custquery="UPDATE geoPoint_temp as temp
join
(
select prod.name, prod.street_num, prod.street_name, prod.UUID,prod.customerUUID, prod.gcomUUID
FROM gisPoints as `prod`
JOIN
(
select t1.gcomUUID , t1.street_num, t1.street_name
FROM geoPoint_temp as t1
) as sub1 on prod.gcomUUID =sub1.gcomUUID and prod.street_num=sub1.street_num
) as sub2 on sub2.gcomUUID =temp.gcomUUID
and sub2.street_num=temp.street_num
AND sub2.street_name LIKE (CONCAT('%',temp.street_name,'%'))
set temp.customerUUID = sub2.customerUUID, temp.UUID=sub2.UUID";
$custre=mysql_query($custquery);
if (!$custre) { echo 'Could not run custre query: ' . mysql_error(); exit; }

Echoing Sorted Multidimensional Array

Ok, so I am creating a web app with php and mysqli.
I have a table friends which is a simple set up:
f_id int(11)
uid int(11)
fids TEXT
now its basically like a row for each user with the fids consisting of a lot of numerical values (other userids) separated by commas like: 1,2,3
so I use this function to get each user's friends:
function getFriends($db, $userid)
{
$q = $db->query("SELECT fids FROM friends WHERE uid='$userid'");
$ar = $q->fetch_assoc();
$friends = $ar['fids'];
$fr = explode(",", $friends);
return $fr;
}
but each posts comments that appear to each of their friends. my problem comes from trying to sort these comments by the time they were posted.
lets say my comments table is:
c_id int(11)
uid int(11)
c_text TEXT
c_time int(11)
I want to be able to get the comments posted by each 'friend' put them all into an array together, then sort them from their c_time value, then all the values from that particular row in the comments table.
The problem comes from my how I've set up my friends table.
I'm using:
$fr = getFriends($db, $userid);
$updates = array();
$i = 0;
foreach( $fr as $friend)
{
// Get Updates from friends and from self
$q = $db->query("SELECT up.*, u.* FROM updates up
LEFT JOIN users u ON u.id = '$friend'
WHERE (up.userid = '$userid') ORDER BY up.up_id DESC");
while($ar = $q->fetch_array(MYSQLI_BOTH))
{
$updates[$i] = $ar;
$i++;
}
}
$sortArray = array();
foreach($updates as $update){
foreach($update as $key=>$value){
if(!isset($sortArray[$key])){
$sortArray[$key] = array();
}
$sortArray[$key][] = $value;
}
}
$orderby = "up_id";
array_multisort($sortArray[$orderby],SORT_DESC,$updates);
$updates_limit = array_slice($updates, 0, 20);
to get the comments from each friend, sorting it by time, then slicing it to the first 20.
However when I var_dump($updates_limit) it takes the last row in the comments table, and then makes it look like each friend posted the same comment.
Can anyone see the problem or a better way of addressing this issue?
I'd completely refactor the friends table to look something more like this: (Also, use english - Characters are cheap :c))
CREATE TABLE friends (
user_id int FOREIGN KEY REFERENCES user(id)
, friend_id int FOREIGN KEY REFERENCES user(id)
, PRIMARY KEY (user_id, friend_id)
);
Then you can take essentially the same comment table:
CREATE TABLE comment (
comment_id int PRIMARY KEY
, user_id int FOREIGN KEY REFERENCES user(id)
, comment_text text
, comment_time datetime
);
And your "query for friend's comments" becomes:
SELECT comment_id, comment.user_id, comment_text, comment_time
FROM friends
INNER JOIN comment
ON comment.user_id = friends.friend_id
WHERE friends.user_id = ? #Target it
ORDER BY comment_time DESC
LIMIT 0, 20;
You can even speed this up by adding a few indexes - like comment(user_id).

MySQL selecting random rows with one unique row within one query

Is it possible to select x random rows from a table, where one of the rows have to be a special row (thereby with a special field value) within one query?
Basically what I'm trying to create is a Guessing Game, where you have x amount of questions, with x amount of possible (checkbox selectable) answers!
This is how I select the answers currently... With 2 query's
$answers = 4; // This is the max amount of answers
// Just for testing the question_id is manually set
$query = "SELECT * FROM `answers` WHERE `question_id` = 0 AND `correct` != 1 ORDER BY RAND() LIMIT " . ($answers - 1) . "";
$query = "SELECT * FROM `answers` WHERE `question_id` = 0 AND `correct` = 1 LIMIT 1";
The "question_id" tells which question we are talking about, and the "correct" just tells if that field is the correct answer
So is it possible to select x random rows from a table, where one of the rows have to be "the correct one", within one query?
may be use UNION ?
$answers = 4;
$query = "SELECT * FROM `answers` WHERE `question_id` = 0 AND `correct` != 1 ORDER BY RAND() LIMIT " . ($answers - 1) . "";
$query .= " UNION ";
$query .= "SELECT * FROM `answers` WHERE `question_id` = 0 AND `correct` = 1 LIMIT 1";
With one single query, you will be able to get the desired result
http://dev.mysql.com/doc/refman/5.0/en/union.html
$query = "SELECT * FROM answers WHERE ... whatever ... AND `question_id` IN (5, 9, 12, 16, 2)
So, you first find the number of rows and then put the correct answer's ID in to the WHERE IN statement. Because you know the one you want, you can just surround it with random IDs from the table and then render them in a random fashion.

Mysql Query has me at a loss

New Post
I ended up avoiding that query entirely. I just couldn't get the results I was looking for. To get the desired results, I came up with this...
Also, if you guys come up with that query, I'd really look to remove this work around.
Thanks for all the help so far!
function get_converstations($data)
{
//get possible senders into an array
$sender_sql = "SELECT DISTINCT `users`.`aid` AS `aid`, `users`.`nickname` AS `nickname`
FROM `users`,`messages`
WHERE `messages`.`sender` = '".$data['aid']."'
AND `messages`.`recipient` = `users`.`aid`";
$query = mysql_query($sender_sql);
if(mysql_num_rows($query) > 0)
{
$sender_data = array();
while ($row = mysql_fetch_array($query)){
$sender_data[$row['aid']] = $row['nickname'];
}
}
//get possible recipients into an array
$recipient_sql = "SELECT DISTINCT `users`.`aid`, `users`.`nickname`
FROM `users`,`messages`
WHERE `messages`.`recipient` = '".$data['aid']."'
AND `messages`.`sender` = `users`.`aid`";
$query = mysql_query($recipient_sql);
if(mysql_num_rows($query) > 0)
{
while ($row = mysql_fetch_array($query)){
$recipient_data[$row['aid']] = $row['nickname'];
}
}
//merge the arrays to overrite any duplicate people.
$no_keys_persons = array_merge($sender_data, $recipient_data);
//create a new array with keys
foreach($no_keys_persons as $aid => $nickname)
{
$persons[] = array(
"aid" => $aid,
"nickname" => $nickname
);
}
//print_r($persons);
//create the conversations array
foreach($persons as $person)
{
$sql = "SELECT * FROM `messages` WHERE `sender` = '".$data['aid']."' AND `recipient` = '".$person['aid']."' OR `sender` = '".$person['aid']."' AND `recipient` = '".$data['aid']."' ORDER BY `id` DESC LIMIT 1";
$query = mysql_query($sql);
if(mysql_num_rows($query) > 0)
{
while($row = mysql_fetch_array($query))
{
$conversations[] = array(
"person_aid" => $person['aid'],
"person_nickname" => $person['nickname'],
"sender" => $row['sender'],
"recipient" => $row['recipient'],
"body" => $row['body'],
"timestamp" => $row['timestamp'],
"ip" => $row['ip']
);
}
}
}
//print_r($conversations);
return $conversations;
}
Then when I call that function on my controller..
//create the data array from url
$data = array(
"aid" => $_GET['aid'],
"nickname" => $_GET['nickname'],
"ip" => $_SERVER['REMOTE_HOST'],
);
//instantiate any classes
include 'db.php';
include 'messages_model.php';
$messages = new messages_model();
$coversations = $messages->get_converstations($data);
foreach ($coversations as $conversation)
{
echo '&conversation=';
echo '&sender='.$conversation['sender'];
if($conversation['sender'] === $data['aid']) { echo '&sender_nickname='.$data['nickname']; } else { echo '&sender_nickname='.$conversation['person_nickname']; }
echo '&recipient='.$conversation['recipient'];
if($conversation['recipient'] === $data['aid']) { echo '&recipient_nickname='.$data['nickname']; } else { echo '&recipient_nickname='.$conversation['person_nickname']; }
echo '&body='.$conversation['body'];
echo '&timestamp='.$conversation['timestamp'];
}
Original Post
I am at a loss here guys. Please see if you can help me put this query together.
I have a table called messages.
CREATE TABLE `db.app`.`messages` (
`id` INT( 32 ) NOT NULL AUTO_INCREMENT ,
`sender` VARCHAR( 64 ) NOT NULL ,
`recipient` VARCHAR( 64 ) NOT NULL ,
`body` TEXT NOT NULL ,
`timestamp` INT( 32 ) NOT NULL ,
PRIMARY KEY ( `id` )
) ENGINE = MYISAM ;
And a table called users.
CREATE TABLE `db.app`.`users` (
`id` INT( 32 ) NOT NULL AUTO_INCREMENT ,
`nickname` VARCHAR( 64 ) NOT NULL ,
PRIMARY KEY ( `id` )
) ENGINE = MYISAM ;
When a message is made, it inserts the ID of the sender into messages.sender, the ID of the recipient into messages.recipient, the message body, and UNIX timestamp. This is working fine.
My problem lies with getting a list of all the unique conversations. (Like text messages on iPhones).
so if we have data like so...
messages table;
id | sender | recipient | body | timestamp
1 | 1234 | 5678 | testing message | 1290233086
2 | 5678 | 1234 | testing reply | 1290233089
users table;
id | nickname
1234 | john
5678 | peter
I would like to be able to generate query results like so...
results;
other_person_id | other_person_nickname | last_message | last_message_timestamp
1234 | john | testing reply | 1290233089
For the life of me, I cant figure out this query....
Something like this should work (sender's id is assumed to be 1 and recipient's id is assumed to be 2):
SELECT users.id, users.nickname, messages.body, messages.timestamp
FROM messages
JOIN users ON messages.recipient = users.id
AND messages.sender = 1
AND messages.recipient = 2
This is a job for a JOIN and will look something like this:
SELECT users.id, users.nickname, messages.body, messages.timestamp
FROM messages JOIN users ON messages.recipient = users.id
Also, you should restructure your table so that you're storing the IDs as the right data type since that will make it much cleaner and will save the database the trouble of having to cast it later (or throw an error):
CREATE TABLE `db.app`.`messages` (
`id` INT( 32 ) NOT NULL AUTO_INCREMENT ,
`sender` INT( 32 ) NOT NULL ,
`recipient` INT( 32 ) NOT NULL ,
`body` TEXT NOT NULL ,
`timestamp` INT( 32 ) NOT NULL ,
PRIMARY KEY ( `id` )
) ENGINE = MYISAM ;
This is what exactly you want:
SELECT users.id AS other_person_id, users.nickname AS other_person_nickname, messages.body AS last_message, messages.timestamp AS last_message_timestamp FROM messages LEFT JOIN users ON (messages.recipient = users.id) ORDER BY messages.id DESC LIMIT 1

Printing out Unique Values From Several Tables in Reverse Chronological Order

I have a MySQL database with multiple tables, each of which have the same structure. The structure is:
id INT(11) NOT NULL auto_increment, site VARCHAR(350) NOT NULL, votes_up BIGINT(9) NOT NULL, votes_down BIGINT(9) NOT NULL, PRIMARY KEY(id), UNIQUE (site)
The PHP code below prints out each unique "site" from across all the tables and sums the "votes_up" it has from all tables. It then lists the top 25 values for "site" (based on total "votes_up") in descending order.
This code works great, but I would like to do the exact same thing based on create time in reverse-chronological order. How do I do this?
Thanks in advance,
John
<?
mysql_connect("mysqlv10", "username", "password") or die(mysql_error());
mysql_select_db("bookfeather") or die(mysql_error());
$result = mysql_query("SHOW TABLES");
$tables = array();
while ($row = mysql_fetch_assoc($result)) {
$tables[] = '`'.$row["Tables_in_bookfeather"].'`';
}
//$subQuery = "SELECT site, votes_up FROM ".implode(" UNION ALL SELECT site, votes_up FROM ",$tables);
$subQueryParts = array();
foreach($tables as $table)
$subQueryParts[] = "SELECT site, votes_up FROM $table WHERE LENGTH(site)";
$subQuery = implode(" UNION ALL ", $subQueryParts);
// Create one query that gets the data you need
$sqlStr = "SELECT site, sum(votes_up) sumVotesUp
FROM (
".$subQuery." ) subQuery
GROUP BY site ORDER BY sum(votes_up) DESC LIMIT 25";
$result = mysql_query($sqlStr);
$arr = array();
echo "<table class=\"samples2\">";
while ($row = mysql_fetch_assoc($result)) {
echo '<tr>';
echo '<td class="sitename2">'.$row["site"].'</td>';
echo '<td>'.$row["sumVotesUp"].'</td>';
echo '</tr>';
}
echo "</table>";
?>
As said in the comments, you will have to add a column which would store the time when the row was created. This column can be of type INT if you are going to store UNIX timestamps or DATETIME if you wish to use date strings. Also, if you don't want to do that, you can use ORDER BY id DESC, because auto-increment columns most often correspond to the chronological order, but that's not a stable solution, because when the AUTO_INCREMENT value runs out of the range of the INT data type, it starts using unused values (e.g. from deleted rows).

Categories