Select from database with count and count based conditions? - php

I have a table with data relating to a user, and two important columns:
refer_count, which is updated when a new entry is made in the table with the referred_by column set to that users user_id, and referred_by which is the user_id of the of the user that referred them.
I want to select the users from the table that have the highest number of referrals after a certain date.
For example:
If there are 3 users, one of which referred the other 2 (lets say users 2 and 3), however user 2 was referred on the 2/12/14, whereas user 3 was referred on the 3/1/15.
If the cutoff is 1/12/14, then user 1 is returned with refer_count set to 2, but if the cutoff is after 2/12/14, then user 1 is returned with refer_count set to 1.
I've been thinking of how to do this, but I can't think of a way that would work. Is there a way?
This is via MySQL.
EDIT: I think I may need to provide for information.
The date registered (register_date) is used as the refer date. I need the refer_count to be updated with the number of users referred after the cutoff, however I need to get the actual user. This is for a 'top referrers' table. I can't figure out why I'm having so much trouble thinking of a way to do this.

SELECT user_id FROM usertable WHERE (referal_date BETWEEN '2014-12-2' AND CURDATE())ORDER BY refer_count DESC;
That's the rough idea.
You should look into normalizing your tables if you're keeping that all in the same table, though. It'd be better to keep referals in a seperate table.

Get the row with the maximum in refer_count with a Date condition for your referal_date such that it's after the certainDate:
SELECT user_id FROM table WHERE refer_count = (SELECT MAX(refer_count) FROM table) AND referal_date>certainDate;
Note that WHERE is before SELECT so it will not get the highest count first, but will filter with the date condition then get the highest count.
Edit: Updated query based on edited question.

Related

Recalculate values in MySQL tables

I have a web application that stores points in a table, and total points in the user table as below:
User Table
user_id | total_points
Points Table
id | date | user_id | points
Every time a user earns a point, the following steps occur:
1. Enter points value to points table
2. Calculate SUM of the points for that user
3. Update the user table with the new SUM of points (total_points)
The values in the user table might get out of sync with the sum in the points table, and I want to be able to recalculate the SUM of all points for every user once in a while (eg. once a month). I could write a PHP script that could loop through each user in the user table and find the sum for that user and update the total_points, but that would be a lot of SQL queries.
Is there a better(efficient) way of doing what I am trying to do?
Thanks...
A more efficient way to do this would be the following:
User Table
user_id
Points Table
id | date | user_id | points
Total Points View
user_id | total_points
A view is effectively a select statement disguised as a table. The select statement would be: SELECT "user_id", SUM("points") AS "total_points" FROM "Points Table" GROUP BY "user_id". To create a view, execute CREATE VIEW "Total Points View" AS <SELECT STATEMENT> where SELECT STATEMENT is the previous select statement.
Once the view has been created, you can treat it as you would any regular table.
P.S.: I don't know that the quotes are necessary unless your table names actually contain spaces, but it's been a while since I worked with MySQL, so I don't remember it's idiosyncrasies.
You have to user Triggers for this, to make the users total points in sync with the user_points table. Something like:
Create Trigger UpdateUserTotalPoints AFTER INSERT ON points
FOR EACH ROW Begin
UPDATE users u
INNER JOIN
(
SELECT user_id, SUM(points) totalPoints
FROM points
GROUP BY user_id
) p ON u.user_id = p.user_id
SET u.total_points = p.totalPoints;
END;
SQL Fiddle Demo
Note that: As noted by #FireLizzard, if these records in the second table, are frequently updated or delted, you have to have other AFTER UPDATE and AFTER DELETE triggers as well, to keep the two tables in sync. And in this case the solution that #FireLizzard will be better in this case.
If you want it once a month, you can’t deal with just MySQL. You have too « logic » code here, and put too logic in database is not the correct way to go. The trigger of Karan Punamiya could be nice, but it will update the user_table on every insert in points table, and it’s not what you seem to want.
For the fact you want to be able to remove points, just add bsarv new negated rows in points, don’t remove any row (it will break the history trace).
If you really want it periodically, you can run a cron script that does that, or even call your PHP script ;)

Order by votes - PHP

I have a voting script which pulls out the number of votes per user.
Everything is working, except I need to now display the number of votes per user in order of number of votes. Please see my database structure:
Entries:
UserID, FirstName, LastName, EmailAddress, TelephoneNumber, Image, Status
Voting:
item, vote, nvotes
The item field contains vt_img and then the UserID, so for example: vt_img4 and both vote & nvotes display the number of votes.
Any ideas how I can relate those together and display the users in order of the most voted at the top?
Thanks
You really need to change the structure of the voting table so that you can do a normal join. I would strongly suggest adding either a pure userID column, or at the very least not making it a concat of two other columns. Based on an ID you could then easily do something like this:
select
a.userID,
a.firstName,
b.votes
from
entries a
join voting b
on a.userID=b.userID
order by
b.votes desc
The other option is to consider (if it is a one to one relationship) simply merging the data into one table which would make it even easier again.
At the moment, this really is an XY problem, you are looking for a way to join two tables that aren't meant to be joined. While there are (horrible, ghastly, terrible) ways of doing it, I think the best solution is to do a little extra work and alter your database (we can certainly help with that so you don't lose any data) and then you will be able to both do what you want right now (easily) and all those other things you will want to do in the future (that you don't know about right now) will be oh so much easier.
Edit: It seems like this is a great opportunity to use a Trigger to insert the new row for you. A MySQL trigger is an action that the database will make when a certain predefined action takes place. In this case, you want to insert a new row into a table when you insert a row into your main table. The beauty is that you can use a reference to the data in the original table to do it:
CREATE TRIGGER Entries_Trigger AFTER insert ON Entries
FOR EACH ROW BEGIN
insert into Voting values(new.UserID,0,0);
END;
This will work in the following manner - When a row is inserted into your Entries table, the database will insert the row (creating the auto_increment ID and the like) then instantly call this trigger, which will then use that newly created UserID to insert into the second table (along with some zeroes for votes and nvotes).
Your database is badly designed. It should be:
Voting:
item, user_id, vote, nvotes
Placing the item id and the user id into the same column as a concatenated string with a delimiter is just asking for trouble. This isn't scalable at all. Look up the basics on Normalization.
You could try this:
SELECT *
FROM Entries e
JOIN Voting v ON (CONCAT('vt_img', e.UserID) = v.item)
ORDER BY nvotes DESC
but please notice that this query might be quite slow due to the fact that the join field for Entries table is built at query time.
You should consider changing your database structure so that Voting contains a UserID field in order to do a direct join.
I'm figuring the Entries table is where votes are cast (you're database schema doesn't make much sense to me, seems like you could work it a little better). If the votes are actually on the Votes table and that's connected to a user, then you should have UserID field in that table too. Either way the example will help.
Lets say you add UserID to the Votes table and this is where a user's votes are stored than this would be your query
SELECT Users.id, Votes.*,
SUM(Votes.nvotes) AS user_votes
FROM Users, Votes
WHERE Users.id = Votes.UserID
GROUP BY Votes.UserID
ORDER BY user_votes
USE ORDER BY in your query --
SELECT column_name(s)
FROM table_name
ORDER BY column_name(s) ASC|DESC

Building a SQL query - Return some rows once, some not

I need to build a SQL query which is beyond my programming abilities.
Okay, here is my request:
Let's say I have a table, with: id, user_id, email and amount columns. This query, should SELECT user_id only once! If I have matched user_id once, the query shall continue, but if the same user_id is matched again in another row, we should skip it.
Here comes the main problem...
Imagining that we grabbed user_id once, and skipped all same other rows with that user_id, now I need to sum all the contents from the amount column for the same user_id.
I think I complicated this a bit, I'll try illustrating my issue:
If this problem is not solvable via SQL only, then a PHP answer would work too.
I'm trying to create a list of users (no duplicate users) and add the amount they paid.
According to the image, user_id 56 paid 90.00 (12 + 45 + 33)
Can someone tell me a way how to achieve this?
Assuming the name of the table is users
SELECT user_id, email, SUM(amount) FROM users GROUP BY user_id

Get the sum of a column value for all rows with same value in different column

I need to know how to handle a pretty complex situation.
I have a system that allows users to vote up or down on comments that others make. I want to create a report of those with the most up votes based on all of their comments. The upvotes were not tracked in the users table, only in the comments table so it needs to go through the comments table and get the value in the vote column and output the sum of all of the vote column values for each userid. It then needs to order these and output the top 10.
Thanks in advance for help
If you post your users and comments table structure I could make a query. But it would be something like this:
SELECT SUM(votes) total, user_id FROM comments GROUP BY user_id ORDER BY total LIMIT 10

Alternative solution for PHP/SQL max array size problem

I have a table bundled among 100 databases in MYSQL (i.e. 1st x rows of the table in database_1, 2nd x rows of the table in database_2, ... , last x rows of the table in database_100)
Each table has a row whenever a user visits a friend for a game.
The columns are iuin, logtime, beuin.
iuin is the user id of the visitor.
beuin is the user id of the friend who was visited.
logtime is when the visit was made.
I would like to find the # of distinct friends who were visited during a week.
There is roughly 300k distinct users who are visited per day.
However, when I extended my code to calculate for a week, I ran out of memory.
My code basically does an SQL query using SELECT DISTINCT beuin for a selected week for the table in each database. I store all the beuin in an array if it's not already stored (so I count distinct friends who were visited), and return the size of the array at the end.
FYI, I can't edit the database around such as joining all the tables in different databases into one table.
Is there any alternative ways i can do this?
Thanks
It's hard to say anything about your without the one. But I think you can solve this problem using mysql. My quick solution:
Create table - CREATE table if not exist users_ids(user_id INT NOT NULL DEAULT 0 PRIMARY KEY(UNIQUE)); in the first db
Truncate users_ids
Run 100 queries like INSERT IGNORE INTO db1.users_ids select distinct user_id from db1.table1;
Select count(*) from users_ids;

Categories