Ad weight, giving advertisers a fair piece of the cake - php

For the project I am currently involved in we have a select number of 'advertiser' groups that can add 'Offers' to our site that are displayed around other products and sometimes are related to the page the customer/buyer is viewing.
The current Ad system is completely random, and gives no preference except when trying to target a specific product. So, if supplier1 has 9 adverts, and supplier2 has 1 advert then supplier1 gets much better value for their money as they get shown 9 out of 10 times.
How we are wanting to change this system is so that supplier1 and supplier2 should technically get the same exposure as each other regardless of the amount of ads they have in the system.
How would you suggest I try and approach this? I have looked at ad weight systems on StackOverflow; however, they don't seem to relate to groups.

SELECT *
FROM advertisments a
INNER JOIN
(
SELECT * FROM suppliers ORDER BY RAND() LIMIT 1
) s ON a.suplplier_id = s.suplplier_id
ORDER BY RAND()
This query first selects one supplier by random (eg, every supplier have the same chance) (oh but if there are suppliers without any advertisement, you should filter them out), then it gets only advertisements of this user (that is how this INNER JOIN works) and orders them by random

Related

Optimize web calculating to speedup web

My system is as follow
right now, I developed website about monitoring sales. Manager will give sales a target each product.. Then sales can input realisation of selling and manager will approve. Web will calculate the achievement and display sales who get the best achiev. Case Preview like in picture.
If the sales about 10 or 20 person its not problem.. But if the sales more than 100 sales like my case in web the sales man about 130 person and 700 approve selling, the result is my web very slow to calculate the best sales and now the sale product still increase.
I use php and CI.
Please give me suggest the best way to solve the problem like this
You can create the Performance View in 1 query:
SELECT sales, (avg( percent_sold )*100) avg_percent_sold
FROM (
SELECT sales, product, SUM(selling) sum_sold, target, SUM(selling)/target as percent_sold
FROM (
SELECT tt.sales, tt.product, tt.target, IFNULL(ts.selling,0) as selling
FROM tbl_target tt
LEFT JOIN tbl_selling ts ON ( ts.sales=tt.sales AND ts.product=tt.product )
ORDER BY NULL
) tbl_sales
GROUP BY sales, product
ORDER BY NULL
) tbl_performance
GROUP BY sales
ORDER BY avg_percent_sold desc
Performance can be optimized by adding indexes on all columns of tbl_target and tbl_selling . Make sure to check using the EXPLAIN query command to find out where indexes are not optimal.

Showing users who liked an item in an item list

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.

limit the number of a field value in search results

I run real state website, which allows different users to add their properties.
When someone searches for a specific criteria, we use a select statement along with the specified conditions to select the matched properties, do the paging bit and display the results.
We use a rating algorithm to rate each property and use it to priorities the displayed properties.
A simplified version:
name type bedrooms user score
green house sale two bedrooms alex 6
Blue one rent three bedrooms jack 6
Blue one sale three bedrooms jack 4
gray one sale three bedrooms jack 6
green one rent three bedrooms jack 6
purple one rent three bedrooms jack 6
green one rent three bedrooms jack 6
green one rent three bedrooms gary 6
Now the problem is that sometimes a few properties have the same score. In these cases I don't want properties from one user to dominate a search result page, I want to set a limit to display a maximum of three properties of any given user in a search result page.
In the example, I don't want the properties owned by jack to dominate the first page, and properties of other users go to second page. This would upset other users and create a bad experience for visitors.
If I wanted to show only one property for a given user, I'd use Group by, but I'm not sure what to limit to a larger number ( three for instance). Is there anything I could do in mysql to achieve this?
EDIT:
Sorry if it wasnt clear enough.
The use field displays the user who added the particular property. A sample query could be
SELECT * FROM properties WHERE type = 'sale' LIMIT 5 ORDER BY score
The result could be five properties, all added by jack. I want to make sure that no more than thee properties added by a particular user, are included in the results. This way properties added by other users would have a chance to be displayed.
Use DISTINCT it will solve your problem.In a table, a column may contain many duplicate values; and sometimes you only want to list the different (distinct) values.The DISTINCT keyword can be used to return only distinct (different) values. Example
SELECT DISTINCT user FROM table_name;
use DISTINCT in your query something like this example
SELECT DISTINCT column_name,column_name from table;
try with this and change string your_table with your table name
SELECT
*
FROM
`your_table`
LEFT JOIN
(SELECT * FROM `your_table` LIMIT 3) as lr on lr.user = `your_table`.user
GROUP BY
user
reference link
UPDATE 2
if you want to order by your score you can use
SELECT
*
FROM
`your_table`
LEFT JOIN
(SELECT * FROM `your_table` ORDER BY score DESC LIMIT 3) as lr on lr.user = `your_table`.user
GROUP BY
user

sql query to do specific matching of demands

so I have a small problem whilst working with SQL and PHP. I have a sql db which has 4 tables namely, customers, requests, products and sellers. Now, each table contains self explanatory data. Such as customers tables lists the customers, ids, names, adresses, emails etc., products table has product id and the name, the requests table shows the request of products made by customers with the request id[pk], custID[fk from customer table], productid[fk in products table], quantites_requested, price_requested. The sellers table contains data such as sellersid[pk], customerid[fk from customer table], productsid[fk in products table], quantites_advertised, price_advertised
Now what I wish to do, is say a customer has requested productid= 1(chocolate),customer id=1, 10kg has been requested for £10.00 So, this data is stored in the DB.
Now, there might be various sellers of chocolate and what I wish to do is fulfil the customers order i.e. 10kg of chocolates in the cheapest way possible. either by combining sellers or selecting one cheapest seller. So, for example, this is the sellers table. NOTE: the sellers table is structured by following: sellerid[pk],customerid[fk],productid[fk],quantity_advertised, price_advertised. so here are some example sellers...
1,2,1,4.00,2.00 -- This means customer id 2 is selling chocolates, for 4KG's #£2.00
2,3,1,5.00,2.50
3,4,1,1.00,1.00
4,5,1,10.00,6.00
Now we have our requests that state
1,1,1,10.00,5.00 -- i.e. customer1, wants chocolates of 10kg for £5.00...
Now I wish to fulfil the customers 10kg order. So what I need to do is select the cheapest price for the customer. This could be done by selecting sellers 2,3 and 4 with the kg's of 5,4 and 1kg=10kg which would cost a total of £2.00+£2.50+£1.00=£5.50 cheaper then seller 5 which can supply 10KG for 6.00. I believe I would be able to do something like this using an SQL query where it first matches the customer requests product id to the sellers product id i.e. SELECT c.Name, p.Name, s.quantity,s.price WHERE c.id=s.customerid AND p.id=s.productid AND s.quantity WHERE r.productid=s.productid FROM requests r, sellers s
however, how would I be able to select the cheapest option supplier either combined or a single large supplier to take to be the most cost effective? I believe I would need to use the MIN somewhere in the query?
Can someone guide me on how to structure a query as stated. i.e. the full customer request needs to be met in the cheapest way possible.
With your current database design, which I still suggest changing, your query would be something like this:
select s.name SellerName
, p.name ProductName
, s.price_advertised price
from sellers s join products p on s.productid = p.productid
join request r on r.productid = p.productid
join (
select sellerid sid
, min(price_advertised) LowestPrice
from sellers ss join request rr on ss.productid = rr.productid
group by sellerid
) sq on sid = s.sellerid and s.advertised_price = LowestPrice
This is a simple example. You'll have to modify it to incorporate requests with more than one product.

MySQL Select entry with no associated entry(in another table)

I am making an custom online management application for a company where users can manage clients, products, components, providers, etc. Right now I am working on a search form, where users can search for clients using keywords and various options. Now, for the keywords search, I know how to use MySQL FullText, the problem is more related to the extra search options.
So basically, every client can be assigned to one or more categories. A client might also no be assigned to any category. To do so, I use three tables:
"clients" table, which holds the basic information of the clients, such as name, contact info, etc. Each clients has a unique ID
"categories" table, which simply has ID, title and description fields
"categories_assign" table, which has only two fields, "clientId" and "categoryId". It allows me to assign multiple category to a client.
Now, in the search form, the user is able to select categories to search in; multiple categories can be selected at once, as well as a "None" value, which should search clients without category. A client without any category will basically not have any entry in the "categories_assign" table.
That's my problem: I have no problem searching for clients assigned to specific categories, but I can't find a way to search clients Without category..
Here's a simplified version (for readability purposes) of the query that select clients according to the selected categories:
SELECT * FROM clients c, categories_assign a WHERE c.id = a.clientId AND a.categoryId IN(1,7,43,52)
So as you might expect, this select clients which is assigned to categories ID 1, 7, 43 or 52. This does works fine. However, as I stated before, I can't find a way to select clients without categories, ie. clients that do not have any entry in the "categories_assign" table. I want to be able to select specific categories too, at the same time. (for example, search for categories ID 1, 7, 43, 52 AND clients without category). I tried using joins, but without luck.
The only workaround I can think of is to create an entry in the "categories_assign" table with "categoryId" set to 0, meaning "no category", so i'd simply need to search for "categoryId" 0. I'd like to avoid the need of doing this, if possible.
Do anyone has any clues?
Thanks in advance!
SELECT DISTINCT c.*
FROM clients c
LEFT JOIN categories_assign a ON c.id = a.clientId
WHERE a.id IS NULL
OR a.categoryId IN (1, 7, 43, 52)
The a.id IS NULL gets those with no category assignments. The a.categoryId IN (...) gets those assigned to those categories.
If you're writing a query solely to get the uncategorized records, you don't need the DISTINCT clause. That's just there to eliminate duplicate records arising from the possibility that a client may be assigned to multiple categories you're looking for.
SELECT * FROM clients c
WHERE c.id NOT IN (SELECT DISTINCT a.clientID FROM categories_assign a)
This will return all clients who do not appear in the categories_assign table. If you want to also do a search for clients with categories at the same time, just UNION this query and your query.

Categories