Merging multiple rows with same field in column - php

I am making a leaderboard for a game server and would like a hand with something.
I have a table in MySQL that looks like this (There are other columns that exist but I want to ignore)
Is there a way to combine each row with the same value in steam_id_64, but with player_name show the last used steam name (Or current from steam) and is it possible to hide the row where steam_id_64 is BOT?
I have php5 if that is helpful. Thank you for the help.

You have to sum up the different columns using sum() function. To avoid those bot entries you can use a condition to test for player_name is not BOT
select
player_name,
sum(kills) as kills,
sum(deaths) as deaths,
sum(assists) as assists,
sum(head_shots) as headshots,
sum(team_kills) as team_kills,
sum(assists_team_attack) as assist_team_attack,
sum(damage) as damage,
sum(hits) as hits,
sum(shots) as shots,
sum(last_alive) as last_alive
group by steam_id_64
where steam_id_64 != "BOT"
To make later handling convenient I have added "... as field_name" to each sum().
Please try this and refine your question if something is missing.

use != for hide the row where steam_id_64 is BOT
Select * from table where steam_id_64 !='BOT'

Related

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

Mysql Unique Query

I have a programme listing database with all the information needed for one programme packed into one table (I should have split programmes and episodes into their own) Now since there are multiple episodes for any given show I wish to display the main page with just the title names in ascending and chosen letter. Now I know how to do the basic query but this is all i know
SELECT DISTINCT title FROM programme_table WHERE title LIKE '$letter%'
I know that works i use it. But I am using a dynamic image loading that requires a series number to return that image full so how do I get the title to be distinct but also load the series number from that title?
I hope I have been clear.
Thanks for any help
Paul
You can substitute the DISTINCT keyword for a GROUP BY clause.
SELECT
title
, series_number
FROM
programme_table
WHERE title LIKE '$letter%'
GROUP BY
title
, series_number
There are currently two other valid options:
The option suggested by Mohammad is to use a HAVING clause in stead of the WHERE clause this is actually less optimal:
The WHERE clause is used to restrict records, and is also used by the query optimizer to determine which indexes and tables to use. HAVING is a "filter" on the final result set, and is applied after ORDER BY and GROUP BY, so MySQL cannot use it to optimize the query.
So HAVING is a lot less optimal and you should only use it when you cannot use 'WHERE' to get your results.
quosoo points out that the DISTINCT keyword is valid for all listed columns in the query. This is true, but generally people do not recommend it (there is no performance difference *In some specific cases there is a performance difference***)**. The MySQL optimizer however spits out the same query for both so there is no actual performance difference.
Update
Although MySQL does apply the same optimization to both queries, there is actually a difference: when DISTINCT is used in combination with a LIMIT clause, MySQL stops as soon as it finds enough unique rows. so
SELECT DISTINCT
title
, series_number
FROM
programme_table
WHERE
title LIKE '$letter%'
is actually the best option.
select title,series_number from programme_table group by title,series_number having title like '$letter%';
DISTINCT keyword works actually for a list of colums so if you just add the series to your query it should return a set of unique title, series combinations:
SELECT DISTINCT title, series FROM programme_table WHERE title LIKE '$letter%'
Hey thanks for that but i have about 1000 entries with the same series so it would single out the series as well rendering about 999 programmes useless and donot show.
I however found out away to make it unique and show the series number
SELECT * FROM four a INNER JOIN (SELECT title, MIN(series) AS MinPid FROM four WHERE title LIKE '$letter%' GROUP BY title) b ON a.title = b.title AND a.series = b.MinPid
Hopefully it helps anyone in the future and thank you for the replies :)

Assign places in the rating (MySQL, PHP)

I have a MySQL database with the following columns:
id company rating_score rating_place
I have companies and rating scores for each company. So, my db looks like:
id company rating_score rating_place
75 Intel 356.23
34 Sun 287.49
etc.
How can I assign the places (my rating_place column is empty) based on the score using php and mysql?
Thank you!
While Andrew G. Johnson is correct, you may not need to even store this information in the database.
The answer I have for you is simple: "Why do you want to store this in the database?"
If you have actually have a good reason, then you have a few choices based on how static the data is. If the data is created then inserted all at once, then ORDER BY rating_score DESC at the end of your statement should do it (if rating_place is assigned automatically from 1).
Otherwise, I would do something in a dedicated PHP page that, once your 2 columns are read, assigns the rating_place. If you manually enter data into your database, it shouldn't hurt to have to open the page. If data collection is automated, go ahead and throw a call to the "update_places_page" that updates the rating.
Edit:
Another option is just to create a view for rating_score that takes the top 20 and orders reorders them, then select from the new view and the actual table based on rating_score.
If you are just trying sort by highest rating to lowest add this to the end of your SQL query:
ORDER BY rating_score DESC
Or lowest to highest:
ORDER BY rating_score ASC
If you still want to do this your way [which I'd advise against] try this:
UPDATE mytable SET rating_place=(SELECT COUNT(*)+1 FROM mytable tablecheck WHERE tablecheck.rating_score > mytable.rating_score)
How about this:
update mytable set rating_place =(select count(*)+1 from mytable intb where intb.rating_score>mytable.rating_score)
----edit (after comment)
aww sorry, you can't select from the same table that you're updating in mysql, so try it with a temp table:
create table mytemptable as
select #row := #row +1 as place, mytable.id
from mytable, (SELECT #row := 0) r
order by rating_score desc;
and then just a similar update:
update mytable set rating_place = (
select place
from mytemptable
where mytemptable.id=mytable.id
)
after that you can drop that mytemptable.
although if you want to avoid a separate table and you can use php, you can try
$res=mysql_query("select id from mytable order by rating_score desc");
$ratings=array();
while ($r=mysql_fetch_assoc($res)) {
$ratings[]=$r['id'];
}
foreach ($ratings as $key=>$val) {
mysql_query("update mytable set rating_score=".($key+1)." where id=".$val);
}
Just sort by rating! This approach is just wrong as you would have to shift modify all data above a certain rank if you insert something. Bad data structure.
Well if you only insert something once or twice a year you could argue that integer sorting is faster, but well thats just a very minimal difference as sorting is based on Tree indexes and not on comparision.
So I have seen solutions like Andrew G. Johnson's. You could also tweak this further and only update entries with a higher score.
You could also create a trigger that does it automatically for you.
But let me explain why this is wrong:
Its redundant data. Its not atomic and consistent.
In a good atabase design you should always (if possible) store every information only at one point so it can be modified, deleted in an atomic way.
So you can avoid any inconsistencies and complications in the first place.
If you really wan't to "cache" the ranking, do it in your application.
So what are your alternatives to this if you really want to have database fields called like this?
Create a mysql view based on the sorted query.
You can also do caching there AFAIK if thats your goal.
But the better option for caching would be just to let the mysql query cache do the work for you. That would be the very best option.
I see no reason what so ever to do what you are trying to do, only valid arguments against it.
SELECT #row := 0;
UPDATE
`table`
SET
`table`.`rating_place` = (#row := #row +1)
ORDER BY
`table`.`rating_score` DESC;
PS: If you will be sending the queries from PHP, you will need to split the two, since PHP MySQL extension normally allows only single query per call.
yourQueryFunc('SELECT #row := 0;');
yourQueryFunc('
SELECT #row := 0;
UPDATE
`table`
SET
`table`.`rating_place` = (#row := #row +1)
ORDER BY
`table`.`rating_score` DESC;');
I would do a select using the order by desc clause and then update each row with the rating.
It is probably a lot more convenient to work out the place as you go. To do this you would read the values and order by Rating_Score (ASC). Then they would be in order of place as you read them out. If you like, you could then write this back into the table, but this would mean you have to constantly update the place value. If this database is going to be constantly changing from user input or something, I would recommend working out the places as you go. If the table will remain mostly static, you could have a place column.

Mysql: Getting Count of Comma Separated Values With Like

I decided to use favs (id's of users which marked that post as a favorite) as a comma separated list in a favs column which is also in messages table with sender,url,content etc..
But when I try to count those rows with a query like:
select count(id)
from messages
where favs like '%userid%'
of course it returns a wrong result because all id's may be a part of another's
For example while querying for id=1 it also increase the counter for any other content that is favorited by user id 11...
Can you please tell me your idea or any solution to make this system work?
With a few or's, you can have an ugly solution:
select count(id) from messages where favs like 'userid,%' or favs like '%,userid,%' or favs like '%,userid'
There's likely a more elegant solution, but that one will at least return the result you're looking for, I believe.
Is it possible to change your data model such that the association between users and their favorite messages is instead stored in another table?
Storing the associations in a single column negates the advantages of a relational database. You pay a performance cost using the like function, you can no longer store additional data about the relationship, and the data is harder to query.
An alternative model might looking something like this (can't include an image since I'm a new user, but I made one here):
users
- id
messages
- id
favorite_messages
- user_id (foreign key to users.id)
- message_id (foreign key to messages.id)
With that in place, your original query would be simplified to the following:
select count(1) from favorite_messages where user_id = userid
Additionally, you can do things like get a list of a user's favorite messages:
select
*
from
messages
inner join favorite_messages
on messages.id = favorite_messages.message_id
where
user_id = userid
should using this :
SELECT count(id) FROM messages WHERE FIND_IN_SET('userid',favs) > 0
You might have to get the value, explode it with PHP and then count the array.
There are ways to do it in MySQL, but from what I've seen, they are a hassle.

Categories