Joins and Many to Many Relationship in mySQL - php

So I have a database consisting of posts and categories using the following tables
posts (PostID, PostName, PostCategory)
categories (CategoryID, CategoryName)
then I have another table which is there to join the two together as follows
posts_categories (Posts_Category_ID, PostID, CategoryID)
I would like to know how to create an SQL Query for the following commands:
a. Create an SQL Query to populate a single POST with MULTIPLE CATEGORIES
b. Search for all POSTS that CONTAINS MULTIPLE CATEGORIES
ex a. Create Post Named "Post1" with Categories "Digital","Analog","Linear"
ex b. Search for all Posts containing Categories "Digital" and "Linear"
Thanks a lot for your help.

If you are talking about fetching the data then do a join between the tables using the chaining table. Something like below
select p.PostName, c.CategoryName
from posts p inner join post_categories pc on p.PostID = pc.PostID
inner join categories c on c.CategoryID = pc.CategoryID
where c.CategoryName in ('Digitl','Linear')

I don't know if I totally get your question, but the query below should return those entries in the many-to-many table that have more than one categoryId for a single postId. Let me know if this helps:
select *
from posts_categories pc
join (
select postId
from posts_categories
group by postId
having count(categoryId) >1
) x on x.postId = pc.postId
Also, you should have a unique constraint on this table for the postId and categoryId columns or make that your primary key.

Related

fetching images from multiple tables

I am building a small online shop where I have a parent table called 'products' and a child table called 'images'.
I have followed best practice and set up a foreign key constraint with the field product_id establishing a link between both tables.
products
product_id (PK parent table)
product_title
product_category_id
product-price
product_quantity
product_description
long_description
product_image
images
image_id (PK for child table)
product_id (foreign key)
product_image_two
NB: Each product will have 2 images, thus I want to retrieve a product based on its product_id and get the associated images from each table.
ie. the query pulls product_image from 'products' and product_image_two from 'images'
I have trawled through a multitude of posts on here about JOIN and tried to refactor other folks code so far without success.
My Current Statement
<?php
$query = query("SELECT p.* , i.* FROM products p,images i WHERE p.product_id=i.product_id");
confirm($query);
while ($row = fetch_array($query)):
?>
It sounds like what you want is a LEFT JOIN. Using a LEFT JOIN, you can select everything in the product table but only rows from the images table if their corresponding key is present in the products table. So for example, your query could look like:
SELECT p.* , i.*
FROM products p,
LEFT JOIN images i ON
p.product_id = i.product_id
This will return every row in the products table, and a value of null for each column in the images table if no second image exists. Here is a simplified demo of what this does: SQL Fiddle
Try with inner join: according to your explanation it should work
SELECT p.product_id,p.product_image, i.product_image_two FROM products p
inner join images i on
p.product_id=i.product_id

How to build an admin with multi categories in PHP and MySQL?

I'd like to build an administration page for my website, where I could use multiple checkboxes to store / updates many categories for a single post.
So I've created three tables:
POST Table (id (index, auto-increment, not null), subject, content)
CATEGORIES Table (id (index, auto-increment, not null), name)
POST_CATEGORIES Table (id (index, auto-increment, not null), id_cat (foreign key to CATEGORIES.id), id_post (foreign key to POST.id))
(This last table is building relations between the posts and its categories.)
So I'm trying to list the categories with multiple checkboxes. I'm doing this query, but it doesn't work as expected:
$select = $db->query('SELECT categories.id, categories.name, post_categories.id_cat AS fk_cat, post_categories.id_post AS fk_post, post.id AS post_id
FROM `categories`
LEFT JOIN post_categories
ON categories.id = id_cat
LEFT JOIN posts
ON post.id = id_post');
Result is i have a list of categories, but each category name is repeated depending on the number of posts having this category (e.g : if post 1 and post 2 have both category 1, category 1 will appear 2 times in my list).
EDIT :
Thanks for the replies, here is a sqlfiddle to see what's happening...
http://sqlfiddle.com/#!9/4c304/1
(I'm trying to display a list of categories. I won't select * from categories table because in a next step, i'd like the right categories to be checked when i edit a post.)
SELECT DISTINCT c.id
, c.name
, p.id post_id
FROM categories c
LEFT
JOIN post_categories pc
ON c.id = pc.id_cat
LEFT
JOIN posts p
ON p.id = pc.id_post
If i use GROUP BY categories.id, i don't have doubles anymore...
But it only keeps the first post which is "tagged" with the current category. I need to keep all those informations.
Maybe i should filter by a php query? Something like if count(category.id > 1) then only display the one where post_id = current post edit? (sorry if it's not clear, i feel a bit confuses :( )

PHP/MYSQL: Get last record of table in a Join AND row count of the joined table

Okay so I have two tables, a category table and a posts table.
I was hoping to construct a query that would return each category, the last record in the post table for that category (category.id = post.category_id) AND the rowcount of all posts with post.category_id = category.id)
i.e
A list of categories, each with the record of the latest post for the category and the number of posts in that category.
I've tried various joins and subselects, and I have managed to get a list of categories, their post count, and their FIRST post (I need the last).
I have also managed to get a list of categories and their LAST post, but not the post count.
Any help/directions will be much appreciated!
Try this:
SELECT category_id, MAX(post_id) AS post_id, COUNT(*) AS posts
FROM post
GROUP BY category_id
This assumes that you have a column post_id that is strictly increasing and unique.
If you have no such field but you have for example post_date that is not unique, you may get two rows for some categories in rare cases where two posts have exactly the same timestamp.
To also get categories without posts use a LEFT JOIN:
SELECT
category.id AS category_id,
MAX(post.id) AS post_id,
COUNT(post.category_id) AS posts
FROM category
LEFT JOIN post
ON category.id = post.category_id
GROUP BY category.id
If you need more columns from each table you can join the result above with the original tables:
SELECT *
FROM
(
SELECT
category.id AS category_id,
MAX(post.id) AS post_id,
COUNT(post.category_id) AS posts
FROM category
LEFT JOIN post
ON category.id = post.category_id
GROUP BY category.id
) T1
JOIN category
ON T1.category_id = category.id
LEFT JOIN post
ON T1.post_id = post.id
Your descriptions lacking a bit of data (I've made assumptions), but the SQL should be:
SELECT category_id,max(postdate) last,count(id) postcount
FROM posts
GROUP BY category_id
If you need something from the category table then you'll just need to join that in.
Assumptions:
There's a column called postdate in your posts table that reflects the date a post was generated (and so the maximum one is the most recent). You could also do max(id) if there are no dates... although only if there's no change a past post could be updated so it's effectively the more recent (eg id=2 created last week,updated today, id=3 created yesterday).
There's a column called id - a unique id for each post row.
If you need the content of the last post you need to join this back to the posts table.

Querying 2 Tables in a single query

I have a table for posts and a table for categories, what i want is to select posts that are in a specific category. The problem is that the category is stored in another table and not in the posts table, here is the example:
posts
id title body
---------------------------
125 Some title Blah blah
categories
postid category
----------------
125 politic
I want in single query to fetch posts in the politic category by example, what to do?
Use:
SELECT p.id,
p.title,
p.body
FROM POSTS p
JOIN CATEGORIES c ON c.postid = p.id
WHERE c.category = 'politic'
The issue I have with your CATEGORIES table is that storing the category value as a string means the data isn't normalized - you should instead have a CATEGORY table:
CATEGORY
category_id (primary key, auto_increment)
category_description
...and use the category_id value in the CATEGORIES table:
CATEGORIES
category_id (primary key, foreign key to CATEGORY.category_id)
post_id (primary key, foreign key to POSTS.postid)
select p.*
from posts p
inner join categories c
on p.id = c.postid
where
c.category = 'politic'
You need to join the two tables in your query
SELECT *
FROM posts P
INNER JOIN categories C on C.post_id = P.id
WHERE C.category = 'politic'
You can intuitively (this is NOT exactly a technically correct explanation) think of this join as appending the category field to a row in posts with the shared id (this is the 'on C.post_id = P.id'). The WHERE clause indicates that you want only those rows where category is 'politic'.
Inner join is one of several join types. Left join in particularly is another common one, and its difference in this situation is that rows of post without a match in categories would still be listed, but with nulls for the fields from categories. (With inner join such rows would not remain)
http://en.wikipedia.org/wiki/Join_%28SQL%29

PHP /MySQL - *-to-Many Relationships

So, I understand how the relationships work in mysql but I'm having a hard time figuring out how its implemented in my code.
For example, say I have the 3 tables.
Table 1: users - user id, username, user city
Table 2: categories - category id, category name
Table 3: user_categories - user id, category id
If I were to query the database for every user that was in a particular city and list them out with the all of the categories they belong to... How would I do this? Would I need to loop through the results and do a separate query for each user, then list the results? Or, is there some magic query that will return a multidimensional array?
I believe the above would be many-to-many, correct me if I'm wrong....
EDIT In the user_categories table, a user can contain more than 1 category, I'm trying to figure out how to return all of them
Thanks!
You're absolutely right, it is a many-to-many query.
And from what I understand, what you're looking for is the ability to have some kind of hierarchical result to display, meaning for one user, have an array of all the categories he's assigned to...
Couple of things you could do:
Option 1: Query the users table:
SELECT u.user_id, u.username, u.user_city WHERE city = 'somecity';
From the results, get all the user_id's that match, put them in an array.
array(1,3,4,5)
Then execute a query by joining the 2 tables categories and user_categories, and passing the array as a comma separated list in a where in:
SELECT user_categories.user_id, categories.category_name
FROM user_categories INNER JOIN categories ON user_categories.category_id = categories.category_id
WHERE user_categories.user_id IN (1,3,4,5)
This will give you a list of user-id, category name that you can use in your script with the previous results to build your result set
option 2: my preferred, use MySQL's GROUP_CONCAT(http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat).
SELECT users.user_id, users.user_name, GROUP_CONCAT(categories.category_name) AS categories
FROM users
INNER JOIN user_categories ON users.id = users_categories.user_id
INNER JOIN categories ON user_categories.category_id = category.id
WHERE user.user_city = 'somecity'
GROUP BY user.user_id
This will return something like:
user_id username categories
1 u1 cat1, cat2, cat3
2 u2 cat1, cat3
You can specify the separator by using SEPARATOR in group_concat.
You need to JOIN the tables.
If I were to query the database for every user that was in a particular city and list them out with the all of the categories they belong to
SELECT *
FROM users
INNER JOIN user_categories
ON (user_id)
INNER JOIN categories
ON (category_id)
WHERE ...
You could try:
SELECT u.user_id, u.username, u.user_city, c.category_id, c.category_name
FROM users u
INNER JOIN user_categories uc ON u.user_id = uc.user_id
INNER JOIN categories c ON uc.category_id = c.category_id
WHERE u.user_city = 'Cityname';
I haven't tested this, and there might be a more efficient way to do it, but it should work.
If you are unfamiliar with joins in mysql, check this out.

Categories