Complex Queries - Get people with the most referrals - php

I am trying to make some sort of SQL Query where I only get the 10 people with the most referrals, but with minimum 1 referral.
My Table looks like this:
CREATE TABLE IF NOT EXISTS `beta_list` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`email` varchar(250) NOT NULL,
`referrer` int(10) NOT NULL,
`referral_code` int(10) NOT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
I have tried something like this:
SELECT
email,
referral_code as refcode,
(SELECT COUNT(*) FROM beta_list WHERE referrer=refcode) as referrals
FROM
beta_list
WHERE
referrals > 0
ORDER BY
referrals DESC
LIMIT
10
But it just says "Unknown column 'referrals' in 'where clause'".
I am no sql guru, I am only just beginning to learn more complex sql queries, so any help on how to achieve something like this would be deeply appreciated!
Cheers!

Try this - Add an outer query to extract the results from inner query -
select ref.email, ref.refcode, ref.referrals from
(
SELECT
email,
referral_code as refcode,
(SELECT COUNT(*) FROM beta_list WHERE referrer=refcode) as referrals
FROM
beta_list
) as ref
WHERE
ref.referrals > 0
ORDER BY
ref.referrals DESC
LIMIT
10

Give this a go:
SELECT email,referral_code as refcode,count(*) as referrals
FROM beta_list
WHERE referrer = referral_code
GROUP BY email,referral_code
ORDER BY referrals DESC
LIMIT 10;

Related

Select last distinct pair

Ok we have inbox table where we keep messages that users send to each other. Here is the table:
CREATE TABLE IF NOT EXISTS `inbox` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`fromid` int(10) unsigned NOT NULL DEFAULT '0',
`toid` int(10) DEFAULT NULL,
`message` text CHARACTER SET utf8 NOT NULL,
`time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY `toid` (`toid`),
KEY `fromid` (`fromid`),
KEY `fromid_2` (`fromid`,`toid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;
fromid and toid are id's of the users. We have their id's, times when the message is sent. What we need is a query that would return all messages that are not replied by 'our users' (admins).
Table accounts keeps track of users. To simplify:
CREATE TABLE IF NOT EXISTS `accounts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`our` int(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
So basically, we need a query that gives us the users WHOSE messages WERE NOT ANSWERED by admins (our users), their count and the date of the last message they sent to ADMIN, ordered from last to oldest.
So far we only have some basic queries, we didn't come up with anything reasonable that I could post.
Thanks in advance.
EDIT: From what I see we first need to find last interaction from two DISTINCT users in inbox table... then check & filter only those that were sent TO our users
How about this?
SELECT i.* FROM inbox as i
WHERE (i.toid, i.fromid) NOT IN
(SELECT i2.fromid, i2.toid FROM inbox as i2 WHERE i2.`time` >= i1.`time` AND i2.id = 1);
Another way using join:
SELECT DISTINCT i1.*
FROM inbox as i1 LEFT JOIN inbox as i2
ON i1.toid = 1 AND
i1.fromid = i2.toid AND
i1.toid = i2.fromid AND
i1.`time` <= i2.`time`
WHERE i2.id IS NULL;
Two possible solutions presented below: LEFT JOIN solution should perform better.
LEFT JOIN solution
SELECT
i.fromid, COUNT(*) AS unread, MAX(i.time) AS lastmsg
FROM inbox AS i
INNER JOIN accounts AS a
ON i.toid = a.id
LEFT JOIN inbox AS i2
ON i.fromid = i2.toid AND i.toid = i2.fromid AND i.time <= i2.time
WHERE a.our = 1 AND i2.id IS NULL
GROUP BY i.fromid
ORDER BY lastmsg DESC;
NOT IN solution
SELECT
i.fromid, COUNT(*) AS unread, MAX(i.time) AS lastmsg
FROM inbox AS i
INNER JOIN accounts AS a ON i.toid = a.id
WHERE a.our = 1 AND
(i.toid, i.fromid)
NOT IN (SELECT i2.fromid, i2.toid FROM inbox AS i2 WHERE i2.time >= i.time)
GROUP BY i.fromid
ORDER BY lastmsg DESC;

Users ranking table on how many likes/upvotes users had

I'm trying to create a ranking table based on how many likes/upvotes a user had on all his items in total. User in the upvotes table links to id of the user that made the like, but I think you don't need this.
Hopefully by giving these tables everything will get clear.
I think the trick here is to get all the upvotes by each item and merge them together towards a user this item was from to get a total likes for each user and then rank all the users based on this total. Of course doing this will probably be a slow query so I need a very performant way to handle this.
The hard thing is here mainly that the upvotes table doesn't include the user id.
3 tables:
CREATE TABLE `items` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`user_id` int(255) NOT NULL,
`img` varchar(500) NOT NULL,
`message` varchar(200) NOT NULL,
`created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`active` int(11) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=19 DEFAULT CHARSET=latin1;
CREATE TABLE `upvotes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user` int(255) NOT NULL,
`item_id` int(255) NOT NULL,
`created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
CREATE TABLE `users` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL,
`password` binary(60) NOT NULL,
`first_name` varchar(255) NOT NULL,
`last_name` varchar(255) NOT NULL,
`active` int(1) NOT NULL DEFAULT '1',
`created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) ENGINE=MyISAM AUTO_INCREMENT=17 DEFAULT CHARSET=latin1;
I need a performant query giving me the ranking of each user ranked on how many likes they got on all their items?
I managed to write this:
SELECT #rank := #rank + 1 AS rank, m.*
FROM (SELECT
users.first_name as first_name,
users.last_name as last_name,
count(upvotes.item_id) as total
FROM upvotes
INNER JOIN users
ON users.id = (SELECT items.user_id FROM items WHERE items.id = upvotes.item_id LIMIT 1)
GROUP BY users.id
ORDER BY total DESC
) m, (SELECT #rank := 0) r
But I reckon this will be super slow when the database grows...
You can do a simple join query in order to get the total likes for each item of user and order your results with the resulting count in descending order
SELECT u.*,i.*,COUNT(DISTINCT up.user) `total_user_likes_item`
FROM users u
JOIN items i ON(i.user_id = u.id)
JOIN upvotes up ON(up.item_id = i.id)
GROUP BY u.id,i.id
ORDER BY u.id,i.id,total_user_likes_item DESC
Edit from comments For user total likes you remove i.id from group by as below query
SELECT u.*,COUNT(DISTINCT up.user) `total_user_likes_item`
FROM users u
JOIN items i ON(i.user_id = u.id)
JOIN upvotes up ON(up.item_id = i.id)
GROUP BY u.id
ORDER BY total_user_likes_item DESC
I'll try answer your question:
In table users you can add row sum_upvotes. Every time when someone get one like (vote) you will increment this column by:
UPDATE users
SET sum_upvotes = sum_upvotes + 1
;
Of course, you will insert a column in table upvotes.
Finally, you query to select users and order them by upvotes will look like this
SELECT first_name, last_name
FROM users
ORDER BY sum_upvotes
;
Hope this helps.

select 3 items with higher sales

I am wanting to select the 3 biggest selling records with this is my table
CREATE TABLE IF NOT EXISTS `contas` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_service` int(11) DEFAULT NULL,
`data` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=28 ;
the 'id_service' is the main column, the more sales, more records are added with the same 'id_service'.
so how do I do this without using PHP and select in descending order?
I tried this
select id_service, count(*) as id_service
from vendas WHERE id_service is not null
group by id_service order by id_service desc LIMIT 3
You have aliased both columns to the same name. No wonder the query is confused. Try this:
select id_service, count(*) as cnt
from vendas WHERE id_service is not null
group by id_service
order by cnt desc
LIMIT 3;

SQL: Get Number of First Place Scores of User

I have a score table that has the following stucture:
CREATE TABLE IF NOT EXISTS `game_scores` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`game_id` int(11) NOT NULL,
`score` int(11) NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=329 ;
I am trying to generate a list of top players ordered by the number of first place high scores they have.
Assumptions:
Higher scores are better. (Otherwise, use MIN instead of MAX)
The issue here is there are multiple games, denoted by game_id. Therefore each game_id should have its own first-place user(s)
You can get first-place scores by:
SELECT MAX(score) score, game_id
FROM game_scores
GROUP BY game_id
You can then get users with first-place scores by joining on this aggregate. This should also properly include ties:
SELECT s.user_id
FROM game_scores s
INNER JOIN (
SELECT MAX(score) score, game_id
FROM game_scores
GROUP BY game_id
) fp ON s.game_id = fp.game_id AND s.score = fp.score
You can then count these up for each user and order by the count:
SELECT s.user_id, COUNT(*) first_place_scores
FROM game_scores s
INNER JOIN (
SELECT MAX(score) score, game_id
FROM game_scores
GROUP BY game_id
) fp ON s.game_id = fp.game_id AND s.score = fp.score
GROUP BY s.user_id
ORDER BY COUNT(*) DESC
Assuming that a score of 1 means high score, the following should do what you want:
SELECT count(user_id), user_id FROM game_scores WHERE score="1" GROUP BY user_id ORDER BY count(user_id) DESC

Query to find 16 most clicked posts in last 30 days without duplicated categories

I have a seemingly simple task but I cannot seem to find an elegant solution using 1 query...
Problem:
I have a table of recorded 'clicks' on 'posts', where each post is part of a 'category'.
I want to find the 16 highest clicked posts in the last 30 days -- but I want to avoid duplicate categories.
It seems very simple actually, but I seem to be stuck.
I know how to get the most clicked in last 30, but I can't figure out how to avoid duplicate cats.
SELECT cat_id,
post_id,
COUNT(post_id) AS click_counter
FROM cs_coupon_clicks
WHERE time_of_click > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY post_id
ORDER BY click_counter DESC
I tried to get creative/hacky with it... it's close but not correct:
SELECT cat_id,
Max(sort) AS sortid
FROM (SELECT cat_id,
post_id,
COUNT(post_id) AS click_counter,
CONCAT(COUNT(post_id), '-', post_id) AS sort
FROM cs_coupon_clicks
WHERE time_of_click > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY cat_id, post_id) t1
GROUP BY cat_id
ORDER BY cat_id ASC
Any help would be greatly appreciated as I am not really a MySQL expert. I may end up just doing some PHP logic in the end, but I am very curious as to the correct way to approach a problem like this.
Thanks guys.
EDIT (structure):
CREATE TABLE `cs_coupon_clicks` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`src` varchar(255) NOT NULL DEFAULT '',
`cat_id` int(20) NOT NULL,
`post_id` int(20) NOT NULL,
`tag_id` int(20) NOT NULL,
`user_id` int(20) DEFAULT NULL,
`ip_address` char(30) DEFAULT NULL,
`referer` varchar(255) NOT NULL,
`browser` varchar(10) DEFAULT NULL,
`server_var` text NOT NULL,
`time_of_click` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `cat_id` (`cat_id`),
KEY `post_id` (`post_id`),
KEY `user_id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
TEMP WORKING SOLUTION (HACKY):
SELECT
cat_id,
MAX(sort) AS sortid
FROM (
SELECT
cat_id,
post_id,
COUNT(post_id) AS click_counter,
RIGHT(Concat('00000000', COUNT(post_id), '-', post_id), 16) AS SORT
FROM cs_coupon_clicks
WHERE time_of_click > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY cat_id, post_id
) AS t1
GROUP BY cat_id
ORDER BY sortid DESC
There is no easy single query solution to this problem, it's a group-wise maximum kind of problem based on a temporary table (the one with counts) that would require self-joins.
Assuming your database grows big enough (otherwise just go for your php logic) I would go for a statistics table, holding info about categories, posts and click counts:
CREATE TABLE `click_cnts` (
`cat_id` int(20) NOT NULL,
`post_id` int(20) NOT NULL,
`clicks` int(20) NOT NULL,
PRIMARY KEY (`cat_id`,`post_id`),
KEY `cat_id` (`cat_id`,`clicks`)
)
and fill it using the same query as the first one in the question:
INSERT INTO click_cnts(cat_id, post_id, clicks)
SELECT cat_id, post_id, COUNT(post_id) AS click_counter
FROM cs_coupon_clicks
WHERE time_of_click > NOW() - INTERVAL 30 DAY
GROUP BY cat_id,post_id
You could update this table using triggers or running update query periodically (do users really need info up to the very last second? probably not...) and save a lot of processing as finding most clicks for each category on indexed table requires a lot less time using a classic group-wise max approach:
SELECT cg.cat_id, cu.post_id, cg.most_clicks
FROM
( SELECT cat_id, max(clicks) as most_clicks FROM click_cnts
GROUP BY cat_id ) cg
JOIN click_cnts cu
ON cg.cat_id = cu.cat_id
AND cu.post_id = ( SELECT cc.post_id FROM click_cnts cc
WHERE cc.cat_id = cg.cat_id
AND cc.clicks = cg.most_clicks
LIMIT 1 )
ORDER BY cg.most_clicks DESC
LIMIT 16
Shot in the dark here. Did you try Select DISTINCT cat_id

Categories