Selecting a users forum activity while limiting results based on permission - php

I have a forum split into multiple tables: categories, topics and threads.
CREATE TABLE forum_categories (
cat_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
role_id INTEGER UNSIGNED NOT NULL DEFAULT 0,
cat_name VARCHAR(50) NOT NULL,
PRIMARY KEY(cat_id),
FOREIGN KEY (role_id)
REFERENCES roles(role_id)
);
CREATE TABLE forum_topics (
topic_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
cat_id INTEGER UNSIGNED NOT NULL,
topic_name VARCHAR(50) NOT NULL,
topic_desc VARCHAR(100) NOT NULL,
PRIMARY KEY(topic_id),
FOREIGN KEY (cat_id)
REFERENCES forum_categories(cat_id)
);
CREATE TABLE forum_threads (
thread_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
parent_id INTEGER UNSIGNED NOT NULL DEFAULT 0,
topic_id INTEGER UNSIGNED NOT NULL,
user_id INTEGER UNSIGNED NOT NULL,
title VARCHAR(100) NOT NULL,
body TEXT NOT NULL,
create_date DATETIME NOT NULL,
PRIMARY KEY (thread_id),
FOREIGN KEY (parent_id)
REFERENCES forum_threads(thread_id),
FOREIGN KEY (topic_id)
REFERENCES forum_topics(topic_id),
FOREIGN KEY (user_id)
REFERENCES users(user_id)
);
The category table has a field named role_id, which if set to any value other than 0 means that only users with that role are allowed to view or interact with the topics in that category.
The problem I'm facing is when attempting to pull a specific users recent activity for everyone to see. I want to COUNT(*) on the threads table containing the requested user_id, but I need to exclude threads having a topic_id associated with a restricted category unless the user requesting the information has permission.
If viewing a specific thread, I would simply extract the topic_id and check like this:
// validate topic id and check for permission
$forum = new Forum();
$valid_topics = $forum->getTopics();
if (!array_key_exists($topic_id, $valid_topics)) {
// invalid topic id
}
$valid_categories = $forum->getCategories();
$role_id = $valid_categories[$valid_topics[$topic_id]['cat_id']]['role_id'];
if ($role_id == 0 || array_key_exists($role_id, $session_user_roles)) {
// user has permission
}
Now I'm trying to convert my PHP logic into SQL. Here is a pseudo-code example of what I'm after:
SELECT COUNT(*),
(SELECT role_id,
(SELECT cat_id
FROM forum_topics AS t2
WHERE topic_id = t1.topic_id) AS cat_id
FROM forum_categories
WHERE cat_id = t2.cat_id) AS role_id
FROM forum_threads AS t1
WHERE user_id = $user_id AND (role_id != 0 OR FIND_IN_SET(role_id, $session_user_roles) > 0)
Any help, please?

Try this way
SELECT count(*)
FROM forum_threads thr
JOIN forum_topics top ON thr.topic_id = top.topis_id
JOIN forum_categories fc ON top.cat_id = fc.cat_id
WHERE thr.user_id = $user_id
AND fc.role_id IN ( 0, 24, 55, 888, .... list of user roles ... )
In the list of user roles (last condition) always put 0 as a first number, then the rest of his roles.

Related

How to check if values from the table 1 are used in table 2?

I have two tables employees and departments.
Departments:
create table dept (
id INT NOT NULL AUTO_INCREMENT,
dept_name VARCHAR(50) NOT NULL,
PRIMARY KEY (id)
);
Employees:
create table department_master (
id INT NOT NULL AUTO_INCREMENT,
dept_id INT NOT NULL,
emp_name VARCHAR(100) NOT NULL,
PRIMARY KEY (id)
);
I want to prevent departments being deleted from the UI if they are assigned to one of the employees in employee table. Left join is giving me duplicate values.
How do I see if the departments are being used in the employees table.
If you want to prevent a department from being deleted, you can simply add a foreign constraint to the table department_master for dept_id column.
create table department_master (
id INT NOT NULL AUTO_INCREMENT,
dept_id INT NOT NULL,
emp_name VARCHAR(100) NOT NULL,
PRIMARY KEY (id),
constraint con_dm foreign key dept_id references dept( id )
);
It's default behavior is ON DELETE RESTRICT which means that if there is atleast one row present in the department_master for a given dept_id, it can't be deleted from dept table.
If you want to fetch, the department that don't have any employee record, you can use NOT EXISTS:
select *
from dept d
where not exists (
select 1
from department_master m
where d.id = m.dept_id
);
I believe you want a count of the number of employees grouped by the department, like so:
SELECT count(*) as employees, d.dept_name FROM dept AS d LEFT JOIN department_master AS e ON e.dept_id = d.id group by e.dept_id

How do I only select recipes that I have the ingredients for?

I'm executing the following statement:
SELECT DISTINCT
r.Recipe_Name
FROM
USER u
INNER JOIN
UserIngredient ui
ON
u.User_ID = ui.User_ID
INNER JOIN
RecipeIngredient ri
ON
ui.Ingredient_ID = ri.Ingredient_ID
INNER JOIN
Ingredient i
ON
ri.Ingredient_ID = i.Ingredient_ID
INNER JOIN
Recipe r
ON
ri.Recipe_ID = r.Recipe_ID
WHERE
u.User_Session = SessionID
and get every recipe that I have at least one ingredient for and not just the recipes that I have ALL ingredients for. My table-layout is the following:
CREATE TABLE User
(
User_ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
User_Pass TEXT NOT NULL,
User_Name TEXT NOT NULL,
User_Surname TEXT NOT NULL,
User_EMail VARCHAR(255) UNIQUE,
User_Session VARCHAR(50) UNIQUE,
User_Admin BOOLEAN,
User_Newsletter BOOLEAN
);
CREATE TABLE Recipe
(
Recipe_ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
Recipe_Name VARCHAR(255) UNIQUE NOT NULL,
Recipe_Clicks INT,
Recipe_Description TEXT,
Recipe_Image VARCHAR(255) DEFAULT 'https://placehold.it/400x500'
);
CREATE TABLE Ingredient
(
Ingredient_ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
Ingredient_Name VARCHAR(255) UNIQUE,
Ingredient_Description TEXT
);
CREATE TABLE RecipeIngredient
(
RecipeIngredient_ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
RecipeIngredient_Amount DECIMAL(8,2) NOT NULL,
MeasuringUnit_ID INT NOT NULL,
Recipe_ID INT NOT NULL,
Ingredient_ID INT NOT NULL,
FOREIGN KEY (MeasuringUnit_ID) REFERENCES MeasuringUnit(MeasuringUnit_ID),
FOREIGN KEY (Recipe_ID) REFERENCES Recipe(Recipe_ID),
FOREIGN KEY (Ingredient_ID) REFERENCES Ingredient(Ingredient_ID)
);
CREATE TABLE UserIngredient
(
UserIngredient_ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
Ingredient_ID INT NOT NULL,
User_ID INT NOT NULL,
FOREIGN KEY(Ingredient_ID) REFERENCES Ingredient(Ingredient_ID),
FOREIGN KEY(User_ID) REFERENCES User(User_ID)
);
The result I get:
I get every recipe that I have one ingredient
The result I want:
All recipes that I have ALL ingredients.
Here is my SQL file
Try this:
SELECT DISTINCT r.Recipe_Name
FROM Recipe r
CROSS JOIN User u
WHERE
(SELECT COUNT(*)
FROM RecipeIngredients ri
LEFT OUTER JOIN UserIngredient ui
ON ri.Recipe_ID = ui.Recipe_ID
AND ri.Ingredient_ID = ui.Ingredient_ID
AND ui.User_ID = u.User_ID
WHERE ui.UserIngredient_ID IS NULL) = 0

What sql statement can I write to get all the linking tracks to an album

I'm trying to think of an SQL statement I can write up in MySQL in which I can get all the tracks included inside an album. The way I have it set up is Album_id which is the primary key to my album table is linked inside the tracks table in the form of a foreign key. That way each track row is linked to an album in the album table.
I'll further detail below the layout of my tables:
tbl_Albums
As you can see I have the Album_id as the primary key and it's auto_incremented so the id will automatically update when a new row is entered.
CREATE TABLE `tbl_Albums` (
`Album_id` int(11) NOT NULL auto_increment,
`Album_Name` varchar(32) NOT NULL,
`Number_Of_Tracks` int(11) NOT NULL,
`Genre` varchar(32) NOT NULL,
`Artist_id` int(11) NOT NULL,
PRIMARY KEY (`Album_id`),
KEY `Artist_id` (`Artist_id`),
CONSTRAINT `tbl_Albums_ibfk_2`
FOREIGN KEY (`Artist_id`) REFERENCES `tbl_Artist` (`Artist_id`))
tbl_Tracks
Inside the tracks table I set a foreign key with album_id, so I link the tracks to a specific album.
CREATE TABLE `tbl_Tracks` (
`Track_id` int(11) NOT NULL auto_increment,
`Track_Name` varchar(32) NOT NULL,
`Length` time NOT NULL,
`Artist_id` int(11) NOT NULL,
`Album_id` int(11) NOT NULL,
PRIMARY KEY (`Track_id`),
KEY `Artist_id` (`Artist_id`),
KEY `Album_id` (`Album_id`),
CONSTRAINT `tbl_Tracks_ibfk_1`
FOREIGN KEY (`Artist_id`) REFERENCES `tbl_Artist` (`Artist_id`),
CONSTRAINT `tbl_Tracks_ibfk_2`
FOREIGN KEY (`Album_id`) REFERENCES `tbl_Albums` (`Album_id`))
I have the tables linked properly, but I'm trying to think of an MySQL statement that can allow me to show all the tracks linked to a specific album and have come up empty so far. I'm then trying to implement the code into a php form for a user to use to list out the tracks
Any help would be greatly appreciated.
SELECT tbl_Albums.Album_id, tbl_Albums.Album_Name, tbl_Tracks.Track_id, tbl_Tracks.Track_Name, tbl_Tracks.Album_id
FROM tbl_Albums
INNER JOIN tbl_Tracks ON tbl_Albums.Album_id = tbl_Tracks.Album_id WHERE
`tbl_Albums`.`Album_Name`='Your album name'
ORDER BY
`tbl_Tracks`.`Track_id`;
SELECT
`t2`.*
FROM
`tbl_Albums` `t1`
INNER JOIN
`tbl_Tracks` `t2`
ON
`t2`.`Album_id`=`t1`.`Album_id`
WHERE
`t1`.`Album_Name`='Name of Your album'
ORDER BY
`t2`.`Track_id`;
I don't know if I understand your question correct, but a simple select should do the trick?
If you know the album id, for instance 5:
SELECT * FROM `tbl_Tracks` WHERE `Album_id` = 5;
If you know the album name:
SELECT `t`.* FROM `tbl_Tracks` AS `t`
LEFT JOIN `tbl_Albums` AS `a` ON `a`.`Album_id` = `t`.`Album_id`
WHERE `a`.`Album_Name` = 'Thriller';

Three tables inner join

I have three tables "user" , "bidding" and "item".
I need to find the query in order to get the completed item auctions for a buyer. The way how to find this in my database is the following, item.received=1 AND u.userid=X (this X will be filled in from my PHP which gives the userID of the highest bid). (note that received=1 implies that the deadline is over so this check is not necessary anymore).
Short explanation of the system: it is an auction website, where a user places bids on items and on the users personal account page I want to show the amount of auctions which he bought (and are processed, thus completed).
The 3 tables look like this:
CREATE TABLE user (
userid INT NOT NULL AUTO_INCREMENT,
username CHAR(30) NOT NULL UNIQUE,
password CHAR(32) NOT NULL,
firstname CHAR(30) NOT NULL,
lastname CHAR(30) NOT NULL,
gender CHAR(1) NOT NULL,
email CHAR(50) NOT NULL UNIQUE,
birthdate DATE NOT NULL,
addressid INT NOT NULL,
picture CHAR(50),
lastlogin TIMESTAMP NOT NULL,
role CHAR(30),
paymentid INT NOT NULL,
PRIMARY KEY (userid),
FOREIGN KEY (addressid)
REFERENCES address(addressid),
FOREIGN KEY (paymentid)
REFERENCES payment(paymentid)
);
CREATE TABLE item (
itemid INT NOT NULL AUTO_INCREMENT,
name CHAR(40) NOT NULL,
description CHAR(255) NOT NULL,
originalpurchasedate DATE,
deadline TIMESTAMP NOT NULL,
minprice DOUBLE,
received BOOLEAN NOT NULL,
dateadded TIMESTAMP NOT NULL,
openbidding BOOLEAN NOT NULL,
categoryid INT NOT NULL,
ownerid INT NOT NULL,
PRIMARY KEY (itemid),
FOREIGN KEY (categoryid)
REFERENCES category(categoryid),
FOREIGN KEY (ownerid)
REFERENCES user(userid)
);
CREATE TABLE bidding (
userid INT NOT NULL,
itemid INT NOT NULL,
amount DOUBLE,
bidtime TIMESTAMP NOT NULL,
FOREIGN KEY (userid)
REFERENCES user(userid),
FOREIGN KEY (itemid)
REFERENCES item(itemid)
);
The malfunctioning solution I have already is: the result is 3 rows and results being: 3 , 1 , 5. The solution I expect to get only has to be 1 row, containing the number of distinct items.
SELECT DISTINCT COUNT(u.userid) FROM `item` i
INNER JOIN `bidding` b ON i.itemid = b.itemid
INNER JOIN `user` u ON b.userid = u.userid
WHERE i.received=1 AND u.userid=2
GROUP BY i.itemid
You need to change your query to group on userid instead of item id, and count different items instead of different users.
SELECT DISTINCT COUNT(i.itemid) FROM `item` i
INNER JOIN `bidding` b ON i.itemid = b.itemid
INNER JOIN `user` u ON b.userid = u.userid
WHERE i.received=1 AND u.userid=2
GROUP BY u.userid

How to select and sort data with a parent / child relationship

I'm storing threads for a forum with a parent / child relationship as follows:
CREATE TABLE forum_threads (
thread_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
parent_id INTEGER UNSIGNED NOT NULL DEFAULT 0,
topic_id INTEGER UNSIGNED NOT NULL,
user_id INTEGER UNSIGNED NOT NULL,
title VARCHAR(100) NOT NULL,
body TEXT NOT NULL,
create_date DATETIME NOT NULL,
PRIMARY KEY (thread_id),
FOREIGN KEY (parent_id)
REFERENCES forum_threads(thread_id),
FOREIGN KEY (topic_id)
REFERENCES forum_topics(topic_id),
FOREIGN KEY (user_id)
REFERENCES users(user_id)
);
New threads have parent_id = 0, whereas replies have parent_id = the thread_id being replied to.
I want to select the most recently updated (replied to) threads and display the results in a table as follows:
How can I do this?
SELECT * FROM forum_threads
WHERE topic_id = whatever AND parent_id = 0
WHAT NEXT???
I'm not sure if this can be done with pure SQL, or if I should manipulate the results with PHP, or if I should try another approach all together?
Use the below query:
SELECT * FROM forum_threads
WHERE topic_id = whatever AND parent_id = 0
ORDER BY updated_date DESC limit 1;
this will give you the most update record.
You can simply do this in SQl
SELECT ft.* FROM forum_threads AS ft
JOIN forum_threads AS ft1 ON ft.id = ft1.thread_id
WHERE topic_id = whatever AND parent_id = 0
ORDER BY ft.create_date DESC
LIMIT 1

Categories