I have two tables like this:
posts table:
post_id subject
1 abc
2 def
and contents table, of which the post_id column is the foreign key:
content_id post_id content date
1 1 main content 2014-07-31 03:02:00
2 1 reply one 2014-07-31 03:03:20
3 1 reply two 2014-07-31 03:06:32
As you can see, the contents table stores both the post's content and its possible repli(es). Here, in this example, the post_id # 1 has its main content and two replies.
What I want to do is to echo the subject and its content only, but not the replies. So I have coded the query like this:
$query = "SELECT subject, content
FROM posts As p
INNER JOIN contents AS c USING (post_id)
ORDER BY post_id DESC";
$result = mysqli_query ($dbc, $query)
while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
echo "{$row['content']}n\";
}
Then you know what?. It echoes out the latest content of the post, which is the content_id # 3. However, what I wanted to do is to echo the post's content, which is the oldest content_id of the post_id # 1.
Can you help me, please?
UPDATED
It basically selects the minimum date per post in a subquery and uses it as a key for the "normal" query to look for.
SELECT distinct subject, content FROM content c
JOIN post p
on c.post_id = p.post_id
WHERE cast(c.post_id as char)+cast(c.contentdate as char)in
(
SELECT
cast(p.post_id as char)+cast(min(c.contentdate) as char) as KeyValue
FROM post p
INNER JOIN content c on p.post_id = c.post_id
GROUP BY p.post_id
)
This returns me:
subject | content
----------------------
asd | main content
def | main content
MIN(DATE) has to be content, so you need to GROUP the id with the DATE
Join both tables on the basis of post_id and then group on the basis of min(date)
$query = "SELECT p.post_id, p.subject, c.content, c.date
FROM posts p
INNER JOIN contents c on p.post_id = c.post_id
WHERE date = (select min(date)
from contents ct
where ct.post_id = p.post_id
)
GROUP BY post_id,subject,content, date
ORDER BY post_id DESC";
$result = mysqli_query ($dbc, $query)
while ($row = mysqli_fetch_array($r, MYSQLI_ASSOC)) {
echo "{$row['content']}n\";
}
Try this
$query = "SELECT subject, content
FROM posts As p
INNER JOIN contents AS c USING (post_id)
ORDER BY date ASC, post_id DESC";
You need to order results by content_id (or date) too. So the sql query might look like this:
$query = "SELECT subject, content
FROM posts As p
INNER JOIN contents AS c USING (post_id)
ORDER BY post_id DESC, content_id ASC";
And, of course, I assume that the main content has added to database before the replies and you will not alter the row indexes which might lead to situation where the first row with certain post_id isn't the actual content.
So, if you want to print out everything you need to do something in your code. You need to print only the first ones of rows with same post_id:
$last_post_id = 0;
while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
if ($last_post_id != $row['post_id']) {
echo "{$row['content']}n\";
}
$last_post_id = $row['post_id'];
}
After testing all the answers here, I chose the answer of #NoobEditor. However, I have to make it clearer as follows:
$query = "SELECT p.post_id, p.subject, c.content, c.date
FROM posts p
INNER JOIN contents c on p.post_id = c.post_id
WHERE date = (select min(date)
from contents c
where c.post_id = p.post_id
)
GROUP BY c.post_id
ORDER BY p.post_id DESC";
$result = mysqli_query ($dbc, $query)
In which I made it clearer at the GROUP and ORDER BY functions. And use c as alias for contents table, but not need to use c and ct
while ($row = mysqli_fetch_array($r, MYSQLI_ASSOC)) {
echo "{$row['content']}n\";
}
Related
I have a news site and I am trying to include a block of news in it.
see the image please
I created a category call world news and added subcategories. (Travel, News, Art, Bussines)
I display them one by one, I mean I do a query for each news, that means 4 different queries like below :
$sql = "SELECT posts.post_catId,
posts.post_seo_url,
posts.post_desc,
posts.post_type,
posts.post_status,
posts.post_title,
posts.post_image_url,
categories.catId,
categories.catName,
categories.cat_seo_url
FROM posts
LEFT JOIN categories
ON posts.post_catId = categories.catId
WHERE post_catId = catId AND cat_seo_url = 'art'
AND post_status = ?
ORDER BY post_created_at DESC LIMIT 1";
$stmt = $pdo->prepare($sql);
$stmt->execute(['1',]);
if($stmt->rowCount() > 0){
while($row = $stmt->fetch()){
//here
}
}
My question is: is there a way to display them in one query ?
Edit : I want to display 1 news from 4 specific categories with one query instead of 4.
I know I can add a new row to categories table to secify which categories can be displayed.
You 'invert' the query, selecting the categories first and then joining to the posts table, constraining it by the post_seo_url field, like so:
$sql = "SELECT
categories.catId,
categories.catName,
categories.cat_seo_url,
posts.post_catId,
posts.post_seo_url,
posts.post_desc,
posts.post_type,
posts.post_status,
posts.post_title,
posts.post_image_url,
FROM categories
JOIN posts ON posts.post_seo_url = (
SELECT p.post_seo_url FROM posts as p
WHERE categories.catId = p.post_catId
ORDER BY p.post_created_at DESC LIMIT 1
)
WHERE post_status = ?"
Here's a method using ROW_NUMBER() function:
SELECT p.*,
categories.catId,
categories.catName,
categories.cat_seo_url
FROM
(SELECT
ROW_NUMBER() OVER (PARTITION BY posts.post_catId ORDER BY posts.post_created_at DESC) AS RN,
posts.post_catId,
posts.post_seo_url,
posts.post_desc,
posts.post_type,
posts.post_status,
posts.post_title,
posts.post_image_url
FROM posts
WHERE post_status = ? ) p
JOIN categories
ON p.post_catId = categories.catId
WHERE RN=1;
I've made the query on posts table to become a subquery with addition of ROW_NUMBER() function then JOIN it with categories table and added WHERE that only return row number = 1. I've changed the LEFT JOIN to JOIN because I don't see any reason to use LEFT JOIN in this operation. However, if there is a reason, please update it in your question.
Here's a demo fiddle
I have an articles table that and I am displaying it in the homepage in a while loop. Inside the while loop I want to display the comments count and images count for each article.
It is working for me now, but it is three queries in total, I am trying to combine it in the first query and then just display all of them in one while loop. Here is what I am trying to achieve:Articles page
The current format I am following:
//a represents articles table, c represents comments table, i represents image table
$query = mysqli_query($conn, "SELECT a.a_id, a.title, a.datetime, a.user_id FROM a ORDER BY a.datetime DESC");
while($fetch = mysqli_fetch_assoc($query){
$imageQ = msqli_query($conn, "SELECT COUNT(image_path), image_path FROM i WHERE a_id = '$fetch['a_id']'");
$imageFetch = mysqli_fetch_assoc($imageQ);
$commentQ = mysqli_query($conn, "SELECT COUNT(comment_id) FROM c WHERE a_id = '$fetch['a_id']'");
$commentFetch = mysqli_fetch_assoc($commentQ);
}
I want to cram all of these queries into one single query that fetches the article and comments count and image count for each article and the first image.
The images and comments are separate dimensions of the data. So, you have to be careful about how to bring them together. In your case, you can aggregate the values before doing the joins:
SELECT a.a_id, a.title, a.datetime, a.user_id,
i.num_images, c.num_comments
FROM a LEFT JOIN
(SELECT a_id, COUNT(image_path) as num_images
FROM i
GROUP BY a_id
) i
ON i.a_id = a.a_id LEFT JOIN
(SELECT a_id, COUNT(comment_id) as num_comments
FROM c
GROUP BY a_id
) c
ON c.a_id = a.a_id
ORDER BY a.datetime DESC;
You can use mysql nested queries something like
SELECT a.,tab1.,tab2.* FROM a INNER JOIN (SELECT * FROM b ) as tab1 INNER JOIN (SELECT * FROM c) as tab2
Hope this can get you to get desired output.
Thanks
How to query multiple tables using different constraints?
For example, limiting results to 1 row from the first table, but getting all results from the second and then ordering those results by unique id ASC.
For example, something like this:
SELECT p.entry_id AS post_id,
p.topic AS post_topic,
p.body AS post_body,
r.reply AS post_reply
FROM
#should get only one row
(SELECT entry_id, topic, body FROM entry_posts WHERE entry_id = {$id} LIMIT 1) AS p
FULL JOIN
#should get all rows with this entry_id and order them
(SELECT reply, FROM entry_replies WHERE entry_id = {$id} ORDER BY id ASC) AS r
ON p.entry_id = r.entry_id
In this case both tables have a column called entry_id that contain the same values.
SELECT p.entry_id AS post_id,
p.topic AS post_topic,
p.body AS post_body,
r.reply AS post_reply
FROM entry_posts p
LEFT OUTER JOIN entry_replies r ON r.entry_id = p.entry_id
WHERE p.entry_id = {$id}
ORDER BY r.id
I have a posts table with id column and more, and a votes table with post_id, value columns and more. Each post_id can be repeated in the votes table.
Now I want to select the most voted posts (and the number of votes) from the database, and I've tried the next:
$query = "SELECT p, SUM(v.value) FROM {$wpdb->posts} p, wp_wti_like_post v JOIN p.id v.post_id WHERE 1=1";
$myrows = $wpdb->get_results( $query );
var_dump($myrows);
but it retrieves an empty array.
Note: {$wpdb->posts} is the correct table for the posts
You need to group by post.id to get a proper sum per post. Then you can sort descending by that sum and get the first row (= highest value).
SELECT
p.*, /* Not sure if this will work. Maybe you have to
specify exact fields you need, although MySQL
is pretty forgiving. */
SUM(v.value) AS number_of_votes
FROM
{$wpdb->posts} p
INNER JOIN wp_wti_like_post v ON v.post_id = p.id
GROUP BY
p.id
ORDER BY
SUM(v.value) DESC
LIMIT 1";
I haven't tested that, but it should work for you.
SELECT {$wpdb->posts}.*,wp_wti_like_post.*, SUM(wp_wti_like_post.value) as Svalue
FROM {$wpdb->posts}
JOIN wp_wti_like_post ON wp_wti_like_post.post_id = {$wpdb->posts}.id
I have tables tbl_posts and tbl_comments with primary keys post_id and comment_id respectively. I tried this code:
$allPosts=mysql_query("
select c.comment_id, post_id, count(*)
from post u,
comments c
where u.comment_id = c.comment_id
group by comment_id, post_id
LIMIT 10
");
but I have no clue what it does. How do I combine two tables so that the total comments determines the order of the listed posts from tbl_posts?
Try this, it's more readable if you separate per lines and work with joins
select c.comment_id, post_id, count(*)
from post u join comments c
on u.comment_id = c.comment_id
group by comment_id, post_id LIMIT 10
It looks like you have tables named tbl_comment and tbl_post but your query has them listed as just comment and post.
select c.comment_id, post_id, count(*)
from tbl_post u, tbl_comments c
where u.comment_id = c.comment_id
group by comment_id, post_id LIMIT 10
$allPosts=mysql_query("select c.comment_id, post_id, count(*) from tbl_post u, tbl_comments c where u.comment_id = c.comment_id group by comment_id, post_id LIMIT 10");
This just fixes the query so it runs, and does not address any content issues you may have, namely the group by on both (what I am guessing) are primary keys.
** EDIT **
To fix the sorting try:
SELECT tbl_post.comment_id, count(*)
FROM tbl_post, tbl_comments
WHERE tbl_post.comment_id = tbl_comment.comment_id
GROUP BY comment_id LIMIT 10
ORDER BY count(*)
Explanation of your SQL:
You are selecting column comment_id from table comments, column post_id from table post using a inner join, grouping by comment_id, post_id, with a limit of 10 results.
I would try:
$allPosts = mysql_query("SELECT * FROM
(SELECT c.comment_id, u.post_id, COUNT(*) AS 'count' FROM post u
LEFT JOIN comments c ON c.comment_id = u.comment_id
GROUP BY c.comment_id, u.post_id)
ORDER BY count DESC LIMIT 10");