How to order items on an online store by popularity - php

I have a series of items displayed on my homepage, which I want to order by popularity. I have a column called sales, which is a counter for how many sales the product has.
How can I use this and/or other columns to determine what items to display on the homepage? I obviously can't just use sales to search for popular items, because the items with the most sales will dominate the homepage - I want a balanced query.
If possible the query should also only occur within a certain timeframe, for example the past week etc.
Here's my query:
$popular_items = DB::fetch("SELECT * FROM `products` ORDER BY `sales` DESC LIMIT 10");

You have to define "popularity" first: by views, by sales, or by number of counts in shopping carts?
Assuming you have 2 factors: pageview and sales. You can order by these factors with a ratio, with respect to certain "fresh period":
SELECT * FROM `products`
WHERE DATEDIFF(last_update_date, CURDATE()) < 30
ORDER BY (pageview * 0.7 + sales * 0.3) DESC
Something like that (as I don't know the column names in your table)
If you want to have balanced results, you can use:
SELECT * FROM `products` ORDER BY RAND() DESC
which will return random results. You probably need LIMIT too (pointless to list out all products).
Side note:
it is suggested to select useful columns only (avoid using SELECT *)

If you have your sales have same value in some case then you can use
SELECT * FROM `products` ORDER BY sales DESC,rand() LIMIT 3
which will return random results with sales Descending order. I was not sure what is your data you have I just tested at my end its word.

Related

Getting the top scores, but remove duplicate users (SQL)

I'm working on a time based game (where a lower time is better), and I have a leaderboard for it, which stores every playthrough into a SQL database.
I'd like to get the top 15 lowest (best) times from the database, but only show one output for each user, so that the best times of the top 15 users are displayed, but I can't seem to do it while returning all of the information.
The Query I'm using is:
SELECT * FROM `scores` WHERE size='$size' ORDER BY `time` ASC LIMIT 15
Thank you.
If you group your data using the user column, you can use MIN() to isolate the lowest/best time for each users. Finally, you sort by BestTime ASC (so that lower numbers are listed first) and truncate the result set with LIMIT.
Query:
SELECT `user`, MIN(`time`) AS BestTime
FROM `scores`
WHERE `size` = '10x10'
GROUP BY `user`
ORDER BY `BestTime`
LIMIT 15;
SELECT * FROM (SELECT user,size,min(time) as time FROM scores
WHERE size = '10x10'
GROUP BY user, size)
ORDER BY time
LIMIT 15
Selects minimum time for each users and returns top 15 users with their min time score.
You would appear to want something like this:
select s.*
from scores s
where s.score = (select max(s2.score) from scores s2 where s2.userid = s.userid)
order by s.score asc
limit 15;
I have no idea what size is for in your sample query.

MYSQL Query - order by and then select random of the same value

I'm currently looking in MySQL to order results by price, and then output a random one with the highest price. (several will have the same price)
I've had a look at other stackoverflow questions and found people with answers saying that if two results are the same you cannot guarantee which one will be outputted however that sounds like a bit of a glitch/hack, is there anyway to order a table by the highest results and then chose a random one of those results?
$qry="SELECT * FROM campaignInformation ORDER BY campaignInformation.bidPerCustomer DESC LIMIT 1";
two of my "bidPerCustomers" are 0.25 and I would like to to chose a random one of these every time, however not choose one with a bet of 0.11 for example
Thanks in advance guys!
I'm asumming that I will have to make a query and then choose a random one from the results however it would be nice if I could do it in the query to begin with.
If you just want to select any from those with max value, then you can build it up cleanly:
SELECT * FROM campaignInformation C WHERE C.bidPerCustomer = (
SELECT MAX(D.bidPerCustomer) FROM campaignInformation D
)
That'll net you all rows that have max value. You can then select one from that table, LIMIT 1, order by RAND()
SELECT * FROM (
SELECT * FROM campaignInformation C WHERE C.bidPerCustomer = (
SELECT MAX(D.bidPerCustomer) FROM campaignInformation D
)
) AS X
ORDER BY RAND()
LIMIT 1
Every derived table needs an alias, hence the 'AS X'
Make sure you have an index on your bidPerCustomer column, or havoc ensues.

PHP MYSQL search based on rating and timestamp

My search query runs like:
select * from posts p where p.post like '%test%' ORDER BY p.upvotes DESC,
p.unix_timestamp DESC LIMIT 20
If there are more than 20 results for the searched keyword, i find out the minimum timestamp value, store it in a hidden element and run another query to Load More results like:
select * from posts p where p.post like '%test%' and p.unix_timestamp < 1360662045
ORDER BY p.upvotes DESC, p.unix_timestamp DESC LIMIT 20
Whats really happening is that my first query is ignoring (Obviously, my mistake) posts which haven't had any votes(meaning 0 votes) because of my ORDER BY p.upvotes DESC and as a result of this, i noticed that it fetched the first post in the table in the first 20 results, so the minimum timestamp becomes first post's timestamp. Now after this, if i try to fetch the next 20 results which is less than the minimum timestamp, it doesn't give anything.
Right now, i am simply using the upvotes ordering to fetch top records. Should i be using some algorithm like Bayesian Average or some other algorithm?
Please advise how i can improve the queries if i had to stay with current system of ordering or is there any viable and more efficient method i should be using?
P.S. If possible, please refer some resources about the Bayesian Average(it seems to be most used) or some other alternative?
Storing the timestamp when you first do a search and then using that for the next query you could use something like this:-
$sql = "SELECT *
FROM posts p
LEFT OUTER JOIN (SELECT post_id, COUNT(*) FROM post_ratings WHERE timestamp_rated <= $SomeTimeStoredBetweenPages GROUP BY post_id) pr ON p.id = pr.post_id
WHERE p.post like '%test%'
ORDER BY pr.post_ratings DESC, p.unix_timestamp
DESC LIMIT ".(($PageNo - 1) * 20)." 20";
This is very much an example as I have no real idea of you table structures. Also not sure if you just have a row for each up vote, or whether there are down votes to take account of as well.

A function that randomly selects a row from the database!

I am creating an online store website that needs the functionality to select a random product from the database.
The idea is that there will be an advert for a random product that is different each time the webpage loads!
Using PHP, how would I go about doing this?
tbl_products
id
code
title
stock
cost
rrp
These are the rows I need to get access to from the database.
Thanks
A most straightforward solution would be this:
SELECT *
FROM tbl_products
ORDER BY
RAND()
LIMIT 1
However, this becomes less efficient as the number of products grows.
This solution:
Selecting random rows
is more efficient, though it still requires a full table scan.
If you product ids are distributes more or less uniformly, use this:
SELECT p.*
FROM (
SELECT
(
(
SELECT MAX(id)
FROM tbl_products
) -
(
SELECT MIN(id)
FROM tbl_products
)
) * RAND() AS rnd
) q
JOIN tbl_products p
ON id >= rnd
ORDER BY
id
LIMIT 1;
If you have gaps between ids, the products after large gaps will tend to be selected more often.
Instead of id, you may use a special unique column for this purpose which you should fill without gaps in a cron job.
ORDER BY RAND() is a well-known solution that has well-known problems.
If the product ids are a consecutive range of integers and there is a non-trivial number of rows, then it will much better to SELECT MAX(id) FROM products, generate a number of random integers between 1 and the result in PHP, and do SELECT * FROM products WHERE id IN (x, y, z) as a second query. If the ids are almost, but not quite, consecutive, you can adapt this solution to generate more random ids than needed to account for the fact that not all of them might be valid (the more fragmentation there is among ids, the more surplus numbers you should generate).
If the above is not an option, then querying like this will still be better than a pure ORDER BY RAND().
Here's a PHP solution
$range_res = mysql_query( " SELECT MAX(id) AS max_id , MIN(id) AS min_id FROM products ");
$range_row = mysql_fetch_object( $range_res );
$random = mt_rand( $range_row->min_id , $range_row->max_id );
$res = mysql_query( " SELECT * FROM products WHERE id >= $random LIMIT 0,1 ");

Returning rows based on sum of column

I'm trying to retrieve a subset of data based on the sum of a column. The goal is a script to release backordered items. Say we have 100 BO products and get 50 in stock. I'd like to update the oldest orders where the sum of order qty < 50. So something like:
Sample Schema:
Orders Table:
order_id, order_date
order_products Table:
order_product_id, order_id, product_id, product_status, ordered_quantity
select * from products
where products_id=1234
and status=1
and sum(products_qty) < 50;
Where sum(products_qty) is the total returned qty, not just for the individual row. Not sure if this is possible with a single query or even a subquery, but thought I would ask the experts here first. I'm trying to avoid returning all the rows and then manually count up till I reach the limit.
You need use GROUP by and Having
select * from products
where products_id=1234
and status=1
group by YourGroupingFields
Having sum(products_qty) < 50;
based on your info:
select product_id from products
where status=1
group by product_id
Having sum(products_qty) < 50;
will be return product_id for which total quantity less 50
When you're using aggregate functions like SUM() and COUNT(), you can't use them in WHERE clauses. WHERE clauses are applied row-by-row as the database scans the table/indexes, which means that the results of the aggregate functions aren't available yet.
Filtering by aggregate results has to be done using HAVING, which is essentially done as the last step before returning data to the client.
From the sounds of your requirement, you need a running count to be kept, until you've retrieved enough rows/orders to use up the new product being entered. This can't be done with a single query. You'd need to use a server-side variable to keep track of how much product's been "used up" by the individual backorders.
Going off the top of my head, something like this might do the trick:
SET #Available = 50;
SELECT order_id, SUM(ordered_quantity), #Available := #Available - SUM(ordered_quantity) AS available
FROM order_products
WHERE product_id = XXX
GROUP BY order_id, product_id
HAVING available >= 0;
with whatever extra WHERE clauses so you get the oldest backorders first

Categories