I have a table of products:
ID, Name, Time, Creator, Owner, Size, Color, ...
I want to ban certain user from certain products. I thought of 2 solutions.
1) I create a table with info about Banned User:
Product ID, User ID, BannedOrNot
2) Add a column for every user to the product table.
ID, Name, Time, Creator, Owner, Size, Color, ..., User 1, User 2, User 3
There I add if user is banned from this product or not.
I know the 1 solution is better. But I have a really incredible amount of queries to deal with every second. So I want to avoid many queries because of performance.
The user does not select a specific product. The script does select the product automatically depending on things like Size, Color etc. But the problem is it does not know if the user is banned from this product or not.
First solution would require to first get all data from the banned list which belongs to the user who is accessing the product table. And then depending on if user is not banned select the entry from product table.
I am using PHP, MySQL if that matters.
I prefer the first solution. Add only a row for each banned user to the table, and add this to your products queries
In the FROM clause
LEFT JOIN banned_table ON (products.id = banned_table.producs_id
AND banned_table.user_id = <current_user_id>)
In the WHERE clause
... AND banned_table.id IS NULL
This only retrieve one row of banned_table if exists and avoid the query results with banned products.
Hope it works fine for you.
You should create a new table with a Foreign Key to products:
Banned(Product -> Products, User)
You can create it using this Queries:
CREATE TABLE banned(product NOT NULL, user NOT NULL, FOREIGN KEY (product) REFERENCES products(id))
You can then get the products for a single user with one Query:
SELECT *
FROM products
MINUS
SELECT p.Name, p.Time, p.Creator, ..
FROM PRODUCTS p LEFT JOIN BANNED b on (p.id = b.product)
WHERE b.user ='YOURUSER'
Related
I am new to PHP and SQL. I am trying to count how many times product is liked and all the products a particular user has liked.But couldn't get the desired results.
I have 3 tables.
product table:
pro_id pro_info pro_price
user table:
id username password
and pro_likes table:
id user product
In pro_likes table "user" and "product" columns are foreign keys referring to user(id) and product(pro_id) respectively. How can I find out how many times a particular product has been liked and which product a particular user has liked?
I tried the following to find out the former but it return the counts for all the products, not a single one.
$q="SELECT
COUNT(pro_likes.product) AS likes
FROM pro_likes
LEFT JOINT products
ON pro_likes.product = products.pro_id";
$r=mysqli_query($con,$q);
$r1=mysqli_fetch_assoc($r);
echo $r1['likes'];
Can anyone please help?
You need to group your query by pro_likes.product
You also need to grab the product name if you're going to get unique rows, otherwise all you're doing is counting the counts of the join.
SELECT
COUNT(pro_likes.product) AS likes, pro_likes.product as product
FROM pro_likes
LEFT JOINT products
ON pro_likes.product = products.pro_id
GROUP BY pro_likes.product;
SELECT DISTINCT
COUNT([pro_likes.prodcuts]) AS likes,
pro_likes.product AS product
FROM pro_likes
LEFT JOIN products
ON pro_likes.product = products.pro_id
GROUP BY pro_likes.product
thank you all for your help.
I was able to accomplish this with the following code:
select count(pro_likes.product) as likes from pro_likes join products on pro_likes.product=products.pro_id where pro_likes.product='$pro_id'"
where $pro_id is products.pro_id that has been passed through URL from the previous page.
I am creating a site that allows users to view desired 'teams' and can then join them with the click of one button.
I have my users table which contains: user_id, user_name, team_id
Then, I have my teams table which contains: team_id, team_name, team_players
How would I go about having the users to join a group, each user can also only be in 1 team at a time.
If you want each user to be able to join multiple teams, and each team to have multiple users, then you need a "join table."
Table teams_users would contain team_id, user_id. You can make a composite primary key on team_id, user_id (preventing a user from joining the same team twice).
Then you can get a team with:
SELECT * FROM users t1 right join teams_users t2 ON t1.team_id = t2.team_id WHERE t2.team_name = 'the rascals'
Even if you only want players to join one team at a time, you might still want to use the join table in case you ever change your mind. It would be very easy. To only allow one team per user, put a unique constraint on user_id in the join table. If you later decide you want to allow multiple teams, you just remove that constraint.
If a user tries the "join team" action, you simply check for the user_id's existence in the join table.
SELECT * FROM teams_users WHERE user_id = $user_id
If it does exist, you retrieve its matching team_id and tell them, "sorry, you are already in team 'the rascals'. You must leave that team if you want to join another." If they drop their team, you simply do:
DELETE from teams_users WHERE user_id = 5
If they add a team, you just do:
INSERT INTO teams_users ($team_id, $user_id) #// (assuming PHP variables).
The INSERT query will only work if they are not already in a team. If they are you would get an error message. You could also look at "INSERT ON DUPLICATE KEY UPDATE ..." queries. But I would advise against that because you want to warn users before they change teams.
You should start by adding the team_id field to the users table as a foreign key and allow it to be NULL.
Then you would display the team names in an html form with a radio button for each team.
In a PHP file (which should be set to the action of your form) create an if statement based on the values you assigned to each radio button. In each if block, execute a sql UPDATE statement that will add the appropriate group_ID to the right user instance.
Let's imagine I have a databases with two tables, Users and Posts. The first table contains a row for each user, the second table a row for each post that users have written. If I want to display a post count on the users' profiles, which of these two strategies work the best:
Every time a user creates a post I UPDATE the Users table, +1 a field PostCount;
When someone visits the profile I simply run a select statement to get a count of post, for example SELECT COUNT(post_id) FROM Posts WHERE id_user = 100;
In the first case I have to UPDATE a table very often, which it could be bad as I believe a table gets locked when doing the update; in the second case I have to run a count every time the user visits a profile. Which poison is the less bitter? Is there any other way?
I would say that it depends on how many times you will display PostCount, especially for a huge amount of Users. If you are going to display it for 1000+ users on a page that will be called a lot of times, then the first solution should be the best. But you need to do transactions to be sure both tables Posts and Users are updated when adding a new post.
Otherwise, the second solution should be enough, but you should use LEFT OUTER JOIN so that you would get both information from Users and Posts table in only one query. Eg:
SELECT *
FROM Users u
LEFT OUTER JOIN (
SELECT user_id , COUNT(*) AS posts_count
FROM Posts
GROUP BY user_id
) p ON p.user_id = u.id
WHERE u.id = :searched_id
(And anyway you should use a Cache system so that you don't have to do the same SQL query for a same page if shown to several users.)
This is an issue that I've deemed impractical to implement but I would like to get some feedback to confirm.
I have a product and users database, where users can like products, the like data is stored in a reference table with just pid and uid.
The client request is to show 3 users who have liked every product in the product listing.
The problem is, its not possible to get this data in one query for the product listing,
How I once implemented and subsequently un-implemented it was to perform a request for the users who have liked the products during the loop through the product list.
ie.
foreach($prods as $row):
$likers = $this->model->get_likers($row->id);
endforeach;
That works, but obviously results in not only super slow product listings, and also creates a big strain on the database/cpu.
The final solution that was implemented was to only show the latest user who has liked it (this can be gotten from a join in the products list query) and have a link showing how many people have liked, and upon clicking on it, opens a ajax list of likers.
So my question is, is there actually a technique to show likers on the product list, or is it simply not possible to execute practically? I notice actually for most social media sites, they do not show all likers on the listings, and do employ the 'click to see likers' method. However, they do show comments per items on the listing, and this is actually involves the same problem doesn't it?
Edit: mock up attached on the desired outcome. there would be 30 products per page.
By reading your comment reply to Alex.Ritna ,yes you can get the x no. of results with per group ,using GROUP_CONCAT() and the SUBSTRING_INDEX() it will show the likers seperated by comma or whatever separator you specified in the query (i have used ||).ORDER BY clause can be used in group_concat function.As there is no schema information is available so i assume you have one product table one user table and a junction table that maintains the relation of user and product.In the substring function i have used x=3
SELECT p.*,
COUNT(*) total_likes,
SUBSTRING_INDEX(
GROUP_CONCAT( CONCAT(u.firstname,' ',u.lastname) ORDER BY some_column DESC SEPARATOR '||'),
'||',3) x_no_of_likers
FROM product p
LEFT JOIN junction_table jt ON(p.id=jt.product_id)
INNER JOIN users u ON(u.id=jt.user_id)
GROUP BY p.id
Fiddle
Now at your application level you just have to loop through the products and split the x_no_of_likers by separator you the likers per product
foreach($prods as $row):
$likers=explode('||',$row['x_no_of_likers']);
$total_likes= $row['total_likes'];
foreach($likers as $user):
....
endforeach;
endforeach;
Note there is a default 1024 character limit set on GROUP_CONCAT() but you can also increase it by following the GROUP_CONCAT() manual
Edit from comments This is another way how to get n results per group, from this you can get all the fields from your user table i have used some variables to get the rank for product group ,used subquery for junction_table to get the rank and in outer select i have filtered records with this rank using HAVING jt.user_rank <=3 so it will give three users records per product ,i have also used subquery for products (SELECT * FROM product LIMIT 30 ) so the first 30 groups will have 3 results for each,for below query limit cannot be used at the end so i have used in the subquery
SELECT p.id,p.title,u.firstname,u.lastname,u.thumbnail,jt.user_rank
FROM
(SELECT * FROM `product` LIMIT 30 ) p
LEFT JOIN
( SELECT j.*,
#current_rank:= CASE WHEN #current_rank = product_id THEN #user_rank:=#user_rank +1 ELSE #user_rank:=1 END user_rank,
#current_rank:=product_id
FROM `junction_table` j ,
(SELECT #user_rank:=0,#current_rank:=0) r
ORDER BY product_id
) jt ON(jt.product_id = p.id)
LEFT JOIN `users` u ON (jt.`user_id` = u.`id`)
HAVING jt.user_rank <=3
ORDER BY p.id
Fiddle n results per group
You should be able to get a list of all users that have liked all products with this sql.
select uid,
count(pid) as liked_products
from product_user
group by uid
having liked_products = (select count(1) from products);
But as data grows this query gets slow. Better then to maintain a table with like counts that is maintained through a trigger or separately. On every like/dislike the counter is updated. This makes it easy to show the number of likes for each product. Then if the actual users that liked that product is wanted do a separate call (on user interaction) that fetches the specific likes for one product). Don't do this for all products on a page until actually requested.
I am assuming the size of both these tables is non-trivially large. You should create a new table (say LastThreeLikes), where the columns would be pid,uid_1,uid_2 and uid_3, indexed by pid. Also, add a column to your product table called numLikes.
For each "like" that you enter into your reference table, create a trigger that also populates this LastThreeLikes table if the numLikes is less than 3. You can choose to randomly update one of the values anyway if you want to show new users once in a while.
While displaying a product, simply fetch the uids from this table and display them back.
Note that you also need to maintain a trigger for the "Unlike" action (if there is any) to re-populate the LastThreeLikes table with a new user id.
Problem
The problem is the volume of data. From the point of view that you need two integer value as a answer you should forget about building a heavy query from your n<->n relations table.
Solution
Generates a storable representation using the file_put_contents() with append option each time a user likes a product. I don't have enough room to write the class in here.
public function export($file);
3D array format
array[product][line][user]
Example:
$likes[1293][1][456]=1;
$likes[82][2][656]=1;
$likes[65][3][456]=1;
.
.
.
Number of users who like this particular product:
$number_users_like_this_product = count($likes[$idProduct]);
All idUser who like this particular product:
$users_like_this_product = count($likes[$idProduct][$n]);
All likes
$all_likes = count($likes);
Deleting a like
This loop will unset the only line where $idProduct and $IdUser you want. Since all the variables are unsigned integer it is very fast.
for($n=1, $n <= count($likes[$idProduct]), $n++)
{
unset($likes[$idProduct][$n][$idUser]);
}
Conclusion
Get all likes will be easy as:
include('likes.php');
P.S If you want to give a try i will be glad to optimize my stuff and share it. I've created the class in 2012.
I'm trying to figure out the best approach, more specifically which JOIN to use, with my current situation:
I have two tables (entries, users) in my database. I'm querying and displaying all my news entries on one of my pages. With each entry, I'm also posting the entry information such as date, time and the author (or user) who created the entry.
In my "entries" table, I'm only inputting the user's id (user_id) as the post's author. The "users" table has all the author's information, such as name, email, etc.
Which "JOIN" statement would be best for specifically querying the "entries" table to display all my entries but to also grab certain information from my "users" table just by matching the user_id from "entries" to user_id in "users"?
Simple join statment
SELECT e.*,u.* FROM entries e JOIN users u
ON e.user_id = u.user.id
LEFT INNER JOIN probably.
it will display all from entries, and if users is atached to entries will be dispplayed aswell, else the field will be null.