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).
Related
I'm trying to show stuff queried from two tables, but on one html table. Data is shown for the last 30 days, based on which, an html table is being generated.
Currently I'm stuck using two queries and generating two html tables:
$query1 = mysqli_query( $con, "SELECT date, stuff* " );
while( $record = mysqli_fetch_array( $query1 ) ){
echo '<html table generated based on query>';
}
$query2 = mysqli_query( $con, "SELECT date, other stuff*" );
while( $record = mysqli_fetch_array( $query2 ) ){
echo '<another html table generated based on query2>';
}
Is there a possibility to show both queries on one html table instead?
Note that it gets tricky since we have dates on one table which are not necessarily found in the second table or vice-versa.
Thanks for the support guys. So far I'm stuck at this:
SELECT * FROM user_visit_logs
LEFT JOIN surfer_stats ON user_visit_logs.date = surfer_stats.date
UNION
SELECT * FROM user_visit_logs
RIGHT JOIN surfer_stats ON user_visit_logs.date = surfer_stats.date
The query completes, but the 2nd table fields are all null:
Furthermore, it breaks when I add additional clause like:
WHERE user_id = '{$_SESSION['user_id']}' ORDER BY date DESC LIMIT 30
I think you are after FULL OUTER JOIN concept:
The FULL OUTER JOIN keyword returns all rows from the left table (table1) and from the right table (table2)
In which you may use common dates as a shared row.
So the query will get to simple one:
$query = "
SELECT table1.date, stuff
FROM table1
LEFT OUTER JOIN table2 ON table1.date = table2.date
UNION
SELECT table2.date, other_stuff
FROM table1
RIGHT OUTER JOIN table2
ON table1.date = table2.date
";
$result = mysqli_query( $con, $query );
while( $record = mysqli_fetch_array( $result ) ){
echo '<html table generated based on query>';
}
Example
This is an schematic diagram of FULL OUTER JOIN concept:
After running into quite a few bumps with this one, I finally managed to merge 2 columns from each table and also to use where and sort clauses on them with the following query:
( SELECT user_visit_logs.user_id,user_visit_logs.date,unique_hits,non_unique_hits,earned,sites_surfed,earnings FROM user_visit_logs
LEFT OUTER JOIN surfer_stats ON user_visit_logs.user_id = surfer_stats.user_id AND user_visit_logs.date = surfer_stats.date where user_visit_logs.user_id = 23 ORDER BY date DESC LIMIT 30 )
UNION
( SELECT surfer_stats.user_id,surfer_stats.date,unique_hits,non_unique_hits,earned,sites_surfed,earnings FROM user_visit_logs
RIGHT OUTER JOIN surfer_stats ON user_visit_logs.user_id = surfer_stats.user_id AND user_visit_logs.date = surfer_stats.date where user_visit_logs.user_id = 23 LIMIT 30 )
Simplified, "user_visit_logs" and "surfer_stats" were the 2 tables needed to be joined.
Absolutely. Just pop them both into a variable:
$data = '';
$query = mysqli_query($con,"SELECT date, stuff* ");
while($record = mysqli_fetch_array($query)) {
$data.= '<tr><td>--Your Row Data Here--</td></tr>';
}
$query2 = mysqli_query($con,"SELECT date, other stuff*");
while($record = mysqli_fetch_array($query2)) {
$data .= '<tr><td>--Your Row Data Here--</td></tr>';
}
echo "<table>$data</table>";
Instead of using echo in your loop, you're just storing the results in $data. Then, you're echoing it out after all data has been added to it.
As for your second point, it's not a big deal if fields don't exist. If they're null, you'll just have a column that doesn't have data in it.
Here's an example with fake column names:
$data = '';
$query = mysqli_query($con,"SELECT date, stuff* ");
while($record = mysqli_fetch_array($query)) {
$data.= "<tr><td>{$record[id]}</td><td>{$record[first_name]}</td><td>{$record[last_name]}</td></tr>";
}
$query2 = mysqli_query($con,"SELECT date, other stuff*");
while($record = mysqli_fetch_array($query2)) {
$data .= "<tr><td>{$record[id]}</td><td>{$record[first_name]}</td><td>{$record[last_name]}</td></tr>";
}
echo "<table><tr><th>ID</th><th>First Name</th><th>Last Name</th></tr>$data</table>";
I have a feeling I may have misunderstood the need. If so, I apologize. If you can elaborate just a bit more I can change my answer :)
Thanks for helping, first I will show code:
$dotaz = "Select * from customers JOIN contracts where customers.user_id ='".$_SESSION['user_id']."' and contracts.customer_contract = ".$_SESSION['user_id']." order by COUNT(contracts.customer_contract) DESC limit $limit, $pocetZaznamu ";
I need to get the lists of users (customers table) ordered by count of contracts(contracts table)
I tried to solve this by searching over there, but I can't... if you help me please and explain how it works, thank you! :) $pocetZanamu is Number of records.
I need get users (name, surname etc...) from table customers, ordered by number of contracts in contracts table, where is contract_id, customer_contract (user id)..
This should do it where is the column name you are counting.
$id = $_SESSION['user_id'] ;
$dotaz = "Select COUNT(`customer_contract`) AS CNT, `customer_contract` FROM `contracts` WHERE `user_id`=$id GROUP BY `customer_contract` ORDER BY `CNT` DESC";
Depending on what you are doing you may want to store the results in an array, then process each element in the array separately.
while ($row = mysqli_fetch_array($results, MYSQL_NUM)){
$contracts[$row[1]] = $row[0];
}
foreach ($contracts AS $customer_contract => $count){
Process each user id code here
}
Not sure what you are counting. The above counts the customer_contract for a table with multiple records containing the same value in the customer_contract column.
If you just want the total number of records with the same user_id then you'd use:
$dotaz = "Select 1 FROM `contracts` WHERE `user_id`=$id";
$results = $mysqli->query($dotaz);
$count = mysql_num_rows($results);
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).
Hello I would like to query multiple identical tables in my db which has different prefixes and than display the results randomly but somehow I need to track the origin of the item and I couldn't figure out how
I do the query like this because I don't have access to information_schema
$query = "SHOW TABLES FROM mydb WHERE RIGHT( tables_in_mydb, 5 ) = 'table'";
$res = mysql_query($query);
$num = mysql_num_rows($res);
while($row = mysql_fetch_row($res)) {
$numbers = explode('_', $row[0]);
if($num > 0) {
$q = "SELECT `this`, `that`, `something` FROM ".$numbers[0]."_idetinticaltables"; // :)
$r = mysql_query($q);
while($c = mysql_fetch_array($r)) {
/*display the results randomly with an identifier where the come from*/
}
}
}
You could use ORDER BY RAND() to randomly sort it
The following might work:
Get the list of the tables you're interested in. You already do that.
Create a UNION of multiple SELECT statements. Each SELECT statement differs for the table being selected from and you add a column set to the name of the table (so you can identify it later):
(SELECT *, TABLENAME = 'first_name_of_table' FROM first_name_of_table ...)
UNION
(SELECT *, TABLENAME = 'second_name_of_table' FROM second_name_of_table ...)
UNION
...
ORDER BY RAND() LIMIT 10;
Because it is a UNION you can randomize the whole order then. See How can i optimize MySQL's ORDER BY RAND() function? because it is not that trivial to do well, the example above is only to have an ORDER BY and LIMIT clause placed there. With many entries in your tables, it will kill your server.
$aa=array()
while($c = mysql_fetch_array($r))
{
/*display the results randomly with an identifier where the come from*/
$aa[]=$c;
}
echo $aa; // print "Array"
The code below works great. I have a MySQL database that contains book titles classified in different categories. In the code below, the variable "site" represents a book title. Each category is represented by a different table in the MySQL database.
The code below ranks the top 25 book titles (site) by total votes across all categories (MySQL tables). I am trying to exclude blank book titles (i. e. when site = ''). How can I do this?
I have tried inserting WHERE site != '' in a few places but I get an error message. So I guess I'm asking, where can I insert WHERE site != ''?
Thanks in advance,
John
<?
mysql_connect("mysqlv10", "username", "password") or die(mysql_error());
mysql_select_db("database") 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);
// 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>";
?>
You shouldn't have separate tables for each book category. I can't believe you have so many books that any of these tables would grow too large. Any scalability benefits you might gain by splitting the table are offset by the complexity of having to do these UNION queries.
Here's what I'd do:
Unify the tables into one table.
Add a Categories table.
Relate books to categories with a many-to-many table.
Then your SQL query becomes much simpler:
$sqlStr = "SELECT site, votes_up FROM Books
WHERE site IS NOT NULL AND site <> ''
ORDER BY votes_up DESC LIMIT 25";
It's probably safest to put it in the subquery:
$subQueryParts = array();
foreach($tables as $table)
$subQueryParts[] = "SELECT site, votes_up FROM $table WHERE LENGTH(site)";
$subQuery = implode(" UNION ALL ", $subQueryParts);
If possible, you should follow Bill Karwin's advice and store all your books in one table. Dynamic table names are very hard to search and manage, and they do not optimize well.