I have a query which returns posts from a database, the query groups the posts by category and the titles are links to the original post. The returned posts are displayed on a categories page.
The trouble is that whilst the post titles are displayed and grouped by category I find that only the first post_id for each post per category is found and only the first post or post with lowest ID in each category is returned by my link. The link brings you to the original post.
Original posts are displayed on a page called “entries.php”
Example:
Post1 id = 1
Post2 id = 2
Post3 id = 3
All the posts above are grouped by category but if I hover over them they all pickup Post1 id=1 for some reason. Is there something I can do to ensure that each id is found when I hover over them?
Thanks for your time!
Query:
$query = "SELECT category, GROUP_CONCAT(DISTINCT title SEPARATOR '<br />') titles, post_id
FROM categories, posts
WHERE categories.category_id = posts.category_id
GROUP BY category";
$result = mysqli_query($dbc, $query)
or die('Error querying database.');
while ($row = mysqli_fetch_array($result)){
echo "<h2>".$row['category']."</h2>";
echo ''.$row['titles'].'';
echo "<hr />";
}
You should GROUP BY post_id, also use ORDER BY yo sort as you whish.
You're missunderstanding SQL's concept of GROUPing. If you GROUP rows on category_id, what you actually are telling MySQL: I want exactly one row for each category_id, regardless of how many rows there exist. In standard SQL you are only allowed to "SELECT category_id" (and aggregate functions like COUNT, MAX, MIN and so on) when using "GROUP BY category_id". MySQL allows you a little more and arbitrarily selects one hit (the first it finds).
What you probably want is ORDER BY category_id, and then do the grouping in your php script like
$prev_cat_id = null;
while ($row = mysqli_fetch_array($result)) {
if ($prev_cat_id != $row['category_id']) {
do stuff for next category
} else {
do stuff for next post in same category
}
}
remember to do cleanup for last category
Related
in Wordpress/WooCommerce MySQL database i have various shortcodes.
If there is 2 orders in the database then one of the orders is a shop_order and the next one is shop_subscription_order. We find those 2 datas in another tabel by calling post_id, which is named wp_posts. There we have to look on the shop_subscription_order if the order is wc_active. We need one mySQL call that looks on how many rows there is on each customer, if there is 2 rows it has to take post_id on the subscription_order and check if it is wc_active, and print all orders with that status.
A not working example but for show what i mean:
SELECT * from post_meta where meta_key=´_customer_id´ and meta_vlaue=´$customerid´ if number of rows='2' select * from wp_post where post_type='shop_supscription' whit the id form post_meta and post_status='wc-active'
and then echo num_of_rows
video example here:
Https:// nltrading.dk/video.mov
First in the video we show an order with 3 rows, and after we show an order with 2 rows.
We only need the orders with 2 rows, where there is an wc_active as shown in the video. We need to call all of this to count the number of rows where there is an wc_active whit only 2 rows of orders.
Use this like code:
//To show number of rows in table
function test()
{
global $wpdb;
$table_name = $wpdb->prefix . 'mydata';
$count_query = "select count(*) from $table_name";
$num = $wpdb->get_var($count_query);
echo $num . 'Rows Found';
}
My tables are structured as follows:
TAGS (more of a category): id, tag name, description, slug
POSTS: id, title, url ...
POSTSTAGS: id, idPost, idTag
USERS: id, username, userSlug...
VOTES: id, idPost, idUser
Every post can have up to five tags and every user can vote only once. Currently, as the tags are not implemented yet, I retrieve my paginated result set with the following query:
SELECT p.*, u.username, u.userSlug, u.id as userId,
exists (select 1 from votes v where v.idUser=$id AND p.userId=v.idUser AND p.url = v.url) as voted
FROM posts p
JOIN users u ON u.id=p.userId
ORDER BY p.created DESC
LIMIT 10 OFFSET :offset
The query gets ran via PDO and returned in JSON format to an angularjs ng-repeat. The $id is the logged in user's id. I use it in the exists subquery to gray out the vote buttons in my angular view (there is also a check on the server side). If someone clicks the username in the view, he will be taken to a detail page where all the user's posts are shown (userSlug to the rescue).
The next step is to include the tags in the result list and here I stuttered. Each post in the list must contain all the associated tags (tagName, description, slug) and each tag must take you to a details page where all the associated posts for that particular tag are shown.
The first solution that came to mind was to bruteforce my way through this after running the previously mentioned query:
foreach ($postsResult as &$post) {
$sql ="SELECT t.* FROM tags t JOIN poststags pt ON t.id=pt.idTag WHERE pt.idPost=$post->id";
$stmt=$db->prepare($sql);
$stmt->execute();
$tagsResult=$stmt->fetchAll(PDO::FETCH_OBJ);
$post->tags = $tagsResult;
}
$response->write(json_encode($postsResult));
Done, easy peasy! Lot's of queries that will generate a huge amount of strain on the server. And we don't want to do that.
The second solution was to fire another query that fetches all tags associated with the postsResult(s) and then insert the corresponding tags in each post letting PHP do the dirty job.
$sql = "
SELECT t.*,
pt.idPost
FROM tags t JOIN poststags pt ON t.id=pt.idTag
WHERE pt.idPost IN (array of post ids)
";
$stmt=$db->prepare($sql);
$stmt->execute();
$tagsResult = $stmt->fetchAll(PDO::FETCH_OBJ);
foreach ($postsResult as &$post) {
$arr = array();
foreach ($tagsResult as $tag) {
if ($post->id==$tag->idPost) {
$arr[]=$tag;
}
}
$post->tags = $arr;
}
$response->write(json_encode($postsResult));
Is there any better or faster way to do this?
Is there any better or faster way to do this?
If you index $tagsResult by postId, which you can do by using FETCH_GROUP, then you can remove the inner nested loop and grab all tags with a certain postId in constant time:
$sql = "
SELECT pt.idPost, — select idPost first so it’s grouped by this col
t.*
FROM tags t JOIN poststags pt ON t.id=pt.idTag
WHERE pt.idPost IN (array of post ids)
";
$stmt=$db->prepare($sql);
$stmt->execute();
$tagsResult = $smt->fetchAll(\PDO::FETCH_GROUP|\PDO::FETCH_OBJ);
//$tagsResult is now grouped by postId
//see https://stackoverflow.com/questions/5361716/is-there-a-way-to-fetch-associative-array-grouped-by-the-values-of-a-specified-c
foreach($postsResult as &$post) {
if(isset($tagsResult[$post->id])) {
$post->tags = $tagsResult[$post->id];
}
else {
$post->tags = array();
}
}
I am trying to create an accordion menu where each accordion menu pulls the rutines of workouts where it differs by the category column in the db example below;
I got my PDO::db design like this;
id
title
describtion
category
hashtags
video
seen
date_published
how can i pull the info from the database by the category to use it in foreach in my view file. I hope i could make my problem clear enough.
thanks in advance.
You could order by category and then name (make sure you have an index on category, name)
Your Query would be:
SELECT * FROM `workouts` ORDER BY category, name
Then when you iterate you check if the category changed and if, close the sublist and open a new one showing the category first.
Another, slightly cleaner Solution would be to iterate twice, using the first loop to sort in an associative array by category:
$sth = $dbh->prepare("SELECT * FROM workouts");
$sth->execute();
$workouts = array();
/* First Loop */
while(($result = $sth->fetch(PDO::FETCH_ASSOC)) !== FALSE) {
$workouts[$result['category']][] = $result;
}
Now you can loop over $workouts, using the key as the Category.
foreach($workouts as $category => $workoutsByCategory) {
//display category and start subset
foreach($workoutsByCategory as $workout) {
//display workout
}
}
It would be a lot cleaner thou to use a second table which holds the category names and use the id of that table in your workouts table to connect. You can then get the results using join. You might want to look into Relational Database Design and Normalization
Example (not tested) Table Layouts:
table "workouts"
id | category_id | title | description | hashtags | video | seen | date_published
table "categories"
id | title
Then you can get the results using:
SELECT workouts.*, categories.title as category_title FROM workouts LEFT JOIN categories ON (category.id = workouts.category_id)
This will make it easier to change the name of a category amongst other advances ... but you'll read up on that i guess.
I'm having trouble getting this to work correctly on my classifieds website, but basically what I am doing is taking all from an Item table LIMIT 40 and displaying it on page. Now for the hard part, I am taking all from a category table, which contains the names of the categories (each category name has a relative id). I need to print out the name of the category WHEN its id is equal to the id of the item. Hopefully the code will help clarify:
$sql = mysql_query("SELECT * FROM table_item ORDER BY creationtime DESC LIMIT 40");
$sql2 = mysql_query("SELECT * FROM table_category");
$ad_count = mysql_num_rows($sql);
$row2 = mysql_fetch_array($sql2);
if($ad_count > 0){
while($row = mysql_fetch_array($sql)){
$item_categoryId = $row["cid"];
$categoryId = $row2["id"];
$categoryName = $row2["name"];
while($item_categoryId == $categoryId){
$catName = $categoryName;
}
echo $catName;
}
}
Now, there is a little more to the code then what I put up, I tried to keep this short and sweet. Instead of echoing the category name, its actually being put into an HTML table row and there is also a lot more information being put in as well. Everything works fine when I don't do anything with the category name, but when I try to build something to access and compare it, then everything goes to shit.
Also, I should mention that this nested while loop seemed to be the best way to go about this (I have tried many ways) and the error I am getting for this is that "there is an unexpected '}'".
Use joins instead of nested loops:
SELECT
*
FROM
table_item,
INNER JOIN
table_category
ON
table_item.cid=table_category.id
ORDER BY
creationtime DESC
LIMIT 40
If not every item has a corresponding category, use LEFT JOIN instead of INNER JOIN.
Assume, I have the Following Tables in my Database.
POSTS:
id, title, content
CATEGORIES:
id, name
RELATION:
post_id, cat_id
I have been succesfully able to insert the values into these tables, while publishing the post.
I am also, able to display the categories beneath each and every post on my Homepage. But, I Fear the method I am using is very resource intensive.
Here is What I Do in MySQL/PHP.
(You can choose to skip the code, and read its description for better understanding)
//Get all the posts
$database = $connection->prepare("Select * FROM POSTS");
$database->execute(array());
$result = $database->fetchAll(PDO::FETCH_ASSOC);
foreach ($result as $r) {
$articleid = $r['id'];
//Display Post title and other things here
//Get the Category Ids
$database = $connection->prepare("Select * FROM RELATION WHERE post_id = ".$article);
$database->execute(array());
$ret_cat= $database->fetchAll(PDO::FETCH_ASSOC);
//Get names of category, and dislay them
foreach($ret_cat as $rc)
{
$catid = $rc['cat_id'];
$database = $connection->prepare("Select * FROM CATEGORIES WHERE cat_id= ".$catid);
$database->execute(array());
$ret_cat_name= $database->fetch(PDO::FETCH_ASSOC);
echo "<a href='index.php?category=".$rc['cat_id']."'>".$ret_cat_name['name']."</a> ";
}
}//End First ForEach
Select All the the Desired Posts from the POSTS table.
Use a Foreach loop to display each post.
Within the foreach loop, I use another SELECT statement to get the categories from RELATION table by matching the POSTS.id.
Then, I Use another foreach loop to display all the categories.
But, since I have only the category id in the RELATION table and I need to display the name of the Category instead.
So, I use another SELECT statement to get the name of the Category from the CATEGORIES table, by using RELATION.cat_id.
The Code Works Fine, and I get what I want. But, I feel there are a lot of mysql requests being generated. Hence, load would be increased on server.
So, I need to know what I am doing is right or not. Or, if there is a simpler way to do all this?
Why not use JOIN to decrease the number of database round-trips?
For example, first select the desired posts...
SELECT * FROM POSTS WHERE <some filter criteria for posts>
...and then for each returned row...
SELECT CATEGORIES.*
FROM RELATION JOIN CATEGORIES
ON RELATION.cat_id = CATEGORIES.id
WHERE
post_id = <value from the first query>
Or even do everything in a single round-trip...
SELECT POSTS.*, cat_id, name
FROM POSTS
JOIN RELATION
ON POSTS.id = RELATION.post_id
JOIN CATEGORIES
ON RELATION.cat_id = CATEGORIES.id
WHERE
<some filter criteria for posts>
...in which case the client code would need to "merge" the resulting rows that have the same POSTS.id.
BTW, you should almost never do something like...
Select * FROM POSTS
...without a WHERE clause. This simply won't scale as the database grows.