SQL query: Using DISTINCT/UNIQUE and SUM() in one statement - sql

THE PROBLEM
I have four game servers collecting data. Putting data into a single table. Unfortunately this causes four entries per stat for one player.
WHAT I WANT
I want one record and SUM()on certain columns.
I will post a statement that I wrote which doesn't work but you'll get the point of what I would like to accomplish.
SELECT DISTINCT( Name ),
Max(Level),
Class,
Sum(Kills),
Sum(Deaths),
Sum(Points),
Sum(TotalTime),
Sum(TotalVisits),
CharacterID
FROM Characters
WHERE Name LIKE '$value%'
OR CharacterID LIKE '$value'
ORDER BY Name ASC;

Let me start by saying that duplicate rows in your database is truly less than ideal. A fully normalized database makes data much easier to manipulate without having random anomalies pop up.
However, to answer your question, this is simply what dweiss said put into code (using group by):
SELECT
name,
MAX(Level),
Class,
SUM(Kills),
SUM(Deaths),
SUM(Points),
SUM(TotalTime),
SUM(TotalVisits),
CharacterID
FROM
Characters
WHERE
Name LIKE '$value%' OR CharacterID LIKE '$value'
GROUP BY
name,
class,
characterid
ORDER BY
Name ASC
;
I'm assuming name, class, characterID, are all the same for each player, because that's the only way to get those values in there. Otherwise you'll have to use aggregate functions on them as well.

Instead of using distinct, you could group by your non-aggregated fields (ie name, class, characterid). This way you can use your aggregates: max, sum, and you will still have your distinct characters!

Related

How can I select all fields except for those with non-distinct values?

I have a table which represents data for people that have applied. Each person has one PERSON_ID, but can have multiple APP_IDs. I want to select all of the columns except for APP_ID(because its values aren't distinct) for all of the distinct people in the table.
I can list every field individually in both the select and group by clause
This works:
select PERSON_ID, FIRST,LAST,MIDDLE,BIRTHDATE,SEX,EMAIL,PRIMARY_PHONE from
applications
where first = 'Rob' and last='Robot'
group by PERSON_ID,FIRST,LAST,MIDDLE,BIRTHDATE,SEX,EMAIL,PRIMARY_PHONE
But there are twenty more fields that I may or may not use at any given time
Is there any shorter way to achieve this sort of selection without being so verbose?
select distinct is shorter:
select distinct PERSON_ID, FIRST, LAST, MIDDLE, BIRTHDATE, SEX, EMAIL, PRIMARY_PHONE
from applications
where first = 'Rob' and last = 'Robot';
But you still have to list out the columns once.
Some more modern databases support an except clause that lets you remove columns from the wildcard list. To the best of my knowledge, Oracle has no similar concept.
You could write a query to bring the columns together from the system tables. That could simplify writing the query and help prevent misspellings.

SQL: Getting a value multiple times

I have a problem getting the same value multiple times and I don't know what I am doing wrong, it's probably something very simple but nothing seems to work for me, and as I said, I need it for a school project and I have only been doing this for about a week.
This is my code:
select hobby
from preshobby
order by hobby asc
When I click execute I get the same value a couple of times. For example:
Wrestling
Wlking
Walking
Walking
Walking
Walking
Touch Football
Tennis
I need the result to be in ascending order and each value should only appear once.
Use distinct:
select distinct hobby
from preshobby
order by hobby
Note that you don't need to specify asc with order by as ascending is the default sort order in most versions of SQL.
In your table you have probably many entries with repeated hobbies. So you need to group them like this
select hobby
from preshobby
group by hobby order by hobby asc
You are basically selecting all the values of hobbies you have entered in the database column.. Since there are many people with same hobby.. when you query the table for the column, you see repetitive values. Use distinct like this..
select distinct hobby from table Name;
And default order is asc so you need not specify any value unless you need it descending.

DISTINCT in a simple SQL query

When executing SQL queries I have been trying to figure out the following:
In this example:
SELECT DISTINCT AL.id, AL.name
FROM albums AL
why is there a need to specify distinct? I thought that the Id being a primary key was enough to avoid duplicate results.
When you specify distinct you are specifying that you want the whole row to be distinct. For example if you have two rows:
ID=1 and Name='Joe Smith'
ID=2 and Name='Joe Smith'
then your query is going to return both rows because the different ID values make the rows distinct.
However, if you are selecting only the ID column (and it's your primary key) then the distinct is pointless.
If you're trying to find all of the unique names then you'd want to:
SELECT DISTINCT AL.name
FROM albums AL
You are right, in your case there should be no need for the word distinct because you are asking for the id and the name. Now, for sake of example where distinct is necessary, say you had multiple id's with the same name. Let It Be is an album by both the Beatles and the Replacements. And let's say you were using your database to write out labels that only included the names of the albums. The query you would want would be:
select distinct al.name
from albums al;
Sometimes your database is not perfect and it ends up with a bunch of junk data. If the id has not been designated as unique, you might end up with duplicate records, and then you might want to avoid seeing the duplicates in your query results.

SELECT TOP 1 is returning multiple records

I shall link my database down below.
I have a query called 'TestMonday1' and what this does is return the student with the fewest 'NoOfFrees' and insert the result of the query into the lesson table. Running the query should help explain what i mean. The problem im having is my SQL code has 'SELECT TOP 1' yet if the query returns two students who have the same number of frees it returns both these records. Wit this being a timetable planner, it should only ever return one result, i shall also put the code below,
Many thanks
Code:
INSERT INTO Lesson ( StudentID, LessonStart, LessonEnd, DayOfWeek )
SELECT TOP 1 Availability.StudentID, Availability.StartTime,
Availability.EndTime, Availability.DayOfWeek
FROM Availability
WHERE
Availability.StartTime='16:00:00' AND
Availability.EndTime='18:00:00' AND
Availability.DayOfWeek='Monday' AND
LessonTaken IS NULL
ORDER BY
Availability.NoOfFrees;
This happens because Access returns all records in case of ties in ORDER BY (all records returned have the same values of fields used in ORDER BY).
You can add another field to ORDER BY to make sure there's no ties. StudentID looks like a good candidate (though I don't know your schema, replace with something else if it suits better):
ORDER BY
Availability.NoOfFrees, Availability.StudentID;

Take the value of the first row met in non-aggregate expressions

I have a query like this:
SELECT PlayerID, COUNT(PlayerID) AS "MatchesPlayed", Name, Role, Team,
SUM(Goals) As "TotalGoals", SUM(Autogoals) As "TotalAutogoals",
SUM(...)-2*SUM(...)+2*SUM(...) AS Score, ...
FROM raw_ordered
GROUP BY PlayerID
ORDER BY Score DESC
where in raw_ordered each row describes the performance of some player in some match, in reverse chronological order.
Since I'm grouping by PlayerID what I get from this query is a table where each row provides the cumulative data about some player. Now, there's no problem with columns with aggregate functions; my problem is with the Team column.
A player may change team during a season; what I'm interested in here is the last Team he played with, so I'd like to have a way to tell SELECT to take the first value met in each group for the Team column (or, in general, for non-aggregate-function columns).
Unfortunately, I don't seem to find any (easy) way to do this in SQLite: the documentation of SELECT says:
If the expression is an aggregate expression, it is evaluated across all rows in the group. Otherwise, it is evaluated against a single arbitrarily chosen row from within the group.
with no suggestion about how to alter this behavior, and I can't find between the aggregate functions anything that just takes the first value it encounters.
Any idea?
SQLite does not have a 'first' aggregate function; you would have to implement it yourself.
However, the documentation is out of date. Since SQLite 3.7.11, if there is a MIN() or MAX(), the record from which that minimum/maximum value comes is guaranteed to be chosen.
Therefore, just add MAX(MatchDate) to the SELECT column list.
SELECT PlayerID, COUNT(PlayerID) AS "MatchesPlayed", Name, Role,
(SELECT Team FROM raw_ordered GROUP BY PlayerID ORDER BY some_date) AS team,
SUM(Goals) As "TotalGoals", SUM(Autogoals) As "TotalAutogoals",
SUM(...)-2*SUM(...)+2*SUM(...) AS Score, ...
FROM raw_ordered
GROUP BY PlayerID
ORDER BY Score DESC
Presumably you have some way in your table to order the output such that you can use a subquery to achieve your goal.