Get values from database where column is unique - php

I need help with an advanced SQL-query (MSSQL 2000).
I have a table called Result that lists athletics 100 meter race-times. A runner can have several racetimes but I want to show only the best time from each runner.
The Result-table contains three columns, Result_id, athlete_id, result_time. So athlete_id must be unique when I list the values and result_time must be the fastest (lowest) value.
Any ideas?

In SQL Server 2000, you can't use windows functions. You can do this as follows:
select r.*
from result r join
(select athlete_id, min(result_time) as mintime
from result r
group by athlete_id
) rsum
on rsum.athlete_id = r.athlete_id and r.time = rsum.mintime
In more recent versions of SQL Server, you would use row_number().

If you simply need the fastest time for each athlete_id, do this:
select athelete_id, min(result_time) as FastestTime
from result
group by athelete_id
To show additional columns from the result table, you can join back to it like this:
select r.*
from result r
inner join (
select athelete_id, min(result_time) as FastestTime
from result
group by athelete_id
) rm on r.athelete_id = rm.athelete_id and r.result_time = rm.FastestTime

What you want is to use an aggregate function. in this case min() which will select the minumin data from all the rows that have the same data in the other selected columns. This means you also have to us the group by clause. The query below should give you the results you want.
Edit: If you need other columns, just bring them into the select clause, then add them to the group by clause like below:
select althlete_id, result_id, min(result_time) as result_time from result-table group by althlete_id, result_id
select althlete_id, result_id, min(result_time) as result_time, race_date from result-table group by althlete_id, race_date, result_id
Edit: You need to add all the columns into the group by that aren't part of an aggregate function. Aggregate functions are ones like min(), max(), avg() and so on.
Short answer: If you aren't putting a column in brackets, it probably has to be in the group by.

Related

MySQL Join - Sorting data, grouping data

I have two tables:
twitterusers table
twittergrowth Table
I am trying to do JOIN these 2 tables, get all fields from twitteruser and selective fields from twittergrowth, then fetch only the last 3 rows from this data.
Expected Output:
Current Output:
I.e the rows are repeating. I want rows unique by ID or usernames, and the last set of timestamps. So it would be the last 3 rows, which has the most recent timestamps.
The code I could type scribble out is :
SELECT
t1.*,
t2.new_followers_count,
t2.new_friends_count,
t2.new_timestamp
FROM twitterusers t1
JOIN twittergrowth t2 on (t1.username=t2.username)
Searched quite few pages/sites, but cant really figure out how to do it. I would appreciate any help. :)
Additionally, I would like to get a LIMIT parameter added to the final result, so that I can paginate the full result.
First you need to find a maximum new_timestamp (latest) within groups of the same user_id and username in twittergrowth table. This is a classic group-wise maximum problem and the subquery tgmax does that. Then you need to join back the same table (tg this time) to get other columns that aren't in the group by clause of subquery and are not used in aggregate functions (like max()). These columns are new_followers_count and new_friends_count.
If you tried to put them in the select of subquery mysql would return values from an unspecified row from the same group and not necessarily the same as the one with the latest timestamp. This is explained here.
Once you get desired output for twittergrowth table the only thing left is to join twitterusers table to get all other columns.
SELECT tu.*, tg.new_followers_count, tg.new_friends_count, tg.new_timestamp
FROM twitterusers tu
JOIN twittergrowth tg
ON tu.user_id = tg.user_id AND tu.username = tg.username
JOIN
( SELECT tgg.user_id, tgg.username, max(tgg.new_timestamp) as latest_timestamp
FROM twittergrowth tgg
GROUP BY tgg.user_id, tgg.username ) tgmax
ON tg.user_id = tgmax.user_id AND tg.username = tgmax.username
AND tg.new_timestamp = tgmax.latest_timestamp
Note that this query would benefit from a composite index on (user_id,username,new_timestamp) in the twittergrowth table.
You need to group by to achieve your expected output.
GROUP BY id
To limit, or split results into pages, you can simply add LIMIT X,Y where X is the starting record and Y is the total number of records.
So a query to pull the expected results you want, but only the first 10 would be like so:
SELECT
t1.*,
t2.new_followers_count,
t2.new_friends_count,
t2.new_timestamp
FROM twitterusers t1
JOIN twittergrowth t2 on t1.username=t2.username
GROUP BY t1.id
LIMIT 0,10

MySQL Join returning 1 row

I have a problem with an SQL query. This is my first time using advanced SQL operations like this so it could be that I'm missing something basic. I am running this query:
SELECT countries.id,
countries.name,
AVG(users.points) AS average
FROM countries
LEFT JOIN users
ON countries.id = users.country
ORDER BY average DESC
This query is only returning 1 row and it's not following the ORDER BY because the returned value is . My aim with this is to get all the records in the Countries table and get the average of the points awarded to the users from each country. I want it to return those countries which do not have users assigned to them as well. I have done this in 2 queries and it worked but I thought that maybe I could do only one query. What am I missing?
It is only returning one row because it is an aggregation query without a group by. Perhaps you mean:
SELECT c.id, c.name, AVG(u.points) AS average
FROM countries c LEFT JOIN
users u
ON c.id = u.country
GROUP BY c.id, c.name
ORDER BY average DESC;
The AVG() makes this an aggregation query. Without the the group by, SQL interprets it as returning one row summarizing all the rows. MySQL supports an extension to the SQL standard where columns in the select do not have to be in the group by. In most databases, you query would return an error.

How to collect the COUNT(*) of two Distinct queries?

I tried the following query and got an error in MySQL. I kind of know why it's throwing me a syntax error (it's related to the two DISTINCT queries being run on the same line) but still believe that its possible (somehow). The two columns card_type and split_type each contain one of five values ("Attack", "Foundation", "Character", "Asset", "Action"). What I would like to do is create a query that would count a record as a +1 if "Attack" appeared in either card_type or split_type.
SELECT DISTINCT(ufs.card_type), DISTINCT(ufs.split_type), COUNT(*) AS COUNT
FROM jg1_products p
LEFT JOIN jg1_product_types pt ON p.products_type = pt.type_id
LEFT JOIN jg1_products_to_categories ptc ON p.products_id = ptc.products_id
LEFT JOIN jg1_cards_ufs ufs ON ufs.products_id = p.products_id
WHERE type_handler LIKE "%product_cards%"
AND ptc.categories_id = 89
GROUP BY ufs.card_type
I guess I should be a BIT clearer in my explanation:
I was hoping that the SQL query would return the total number of results which have met a condition in either Column A or Column B.
EXAMPLE:
If the word "Attack" appeared in either column A or column B, count that as one.
If the word "Attack" appeared in Column A but Column B contains the word "Foundation", That would be +1 to Attack and +1 to Foundation.
In the end, the function/SQL would return to number of times that word (either of the five possibles) would appear between those two columns. So in short I used the "DISTINCT" command (incorrect I'll add) so that all the distinct values in column A are returned and the same for B. If they match, count that as one.
Q: "a query that would count a record as a +1 if "Attack" appeared in either card_type or split_type."
A: It's not clear what resultset you want to return.
EDIT
Based on your comment/clarification, if I'm understanding this correctly, if the rows returned by the SELECT were something like this example:
card_type split_type
--------- -----------
Attack Attack
Attack Foundation
Attack Crescendo
Foundation Foundation
You want a resultset something like this:
Attack 3
Foundation 2
Crescendo 1
You want 3 returned for "Attack", because three rows had the value 'Attack' in either card_type or split_type. That is, you don't want to return a count of 4, the number of times the value appeared.
To get that result, using a COUNT aggregate, I would run this as two separate queries, and combine the results of the two queries using a UNION ALL set operator. The first query would get a count by just card_type, the second query would get a count by split_type. The "trick" would be for the second query to exclude any rows where the split_type matches the card_type.
The two combined queries would be used as an inline view, the outer query would combine the separate counts using a SUM() aggregate function.
I would do the query using a form something like this:
SELECT c.type
, SUM(c.cnt) AS cnt
FROM ( SELECT ufs.card_type AS `type`
, COUNT(1) AS cnt
FROM ...
GROUP BY ufs.card_type
UNION ALL
SELECT ufs.split_type AS `type`
, COUNT(1) AS cnt
FROM ...
AND NOT (ufs.split_type <=> ufs.card_type)
GROUP BY ufs.split_type
) c
GROUP BY c.type
You'd plug in the row source of the original query two times, replacing the ... in the query above.
Previous answer:
Assuming that you have a SELECT that returns the rows you want checked, one "trick" is to use an expression in the SELECT list to perform a conditional test, and return either a zero or one, and then use SUM() aggregate to return a "count" of the records that meet the specification..
SELECT SUM(IF(ufs.card_type LIKE '%Attack%' OR ufs.split_type LIKE '%Attack%',1,0)) AS cnt
FROM jg1_products p
LEFT JOIN jg1_product_types pt ON p.products_type = pt.type_id
LEFT JOIN jg1_products_to_categories ptc ON p.products_id = ptc.products_id
LEFT JOIN jg1_cards_ufs ufs ON ufs.products_id = p.products_id
WHERE type_handler LIKE "%product_cards%"
AND ptc.categories_id = 89
This query returns a single row, unlike your original query that returns multiple rows. (Again, it's not clear what resultset you want returned; if you actually want to return a count for each distinct card_type, which would be returned if we included a GROUP BY ufs.card_type clause.
MySQL also provides a convenient shorthand for the boolean expression: the evaluation of a boolean expression returns 1 if TRUE, 0 if FALSE, and NULL if NULL. So this expression:
SELECT SUM(ufs.card_type LIKE '%Attack%' OR ufs.split_type LIKE '%Attack%')
FROM ...
is equivalent to the expression in the query above, except for the handling of NULL values.
It's not clear whether you want to check if the column "contains" the string 'Attack' as part of the string, or is the entire string; to check if the value of the column is exactly equal to 'Attack', use the equality comparison instead of LIKE
SELECT SUM(ufs.card_type = 'Attack' OR ufs.split_type = 'Attack') AS cnt
FROM ...
NOTE
DISTINCT is not a function, it's a keyword.
The valid syntax is SELECT DISTINCT expr1, expr2, ... exprN FROM ....
It's invalid to include the DISTINCT keyword multiple times after the SELECT keyword, or in a position other than immediately following SELECT. (The DISTINCT keyword can also be included the COUNT() aggregate function, e.g. SELECT COUNT(DISTINCT expr), but that's entirely different than SELECT DISTINCT.
The parens are entirely ignored. That is, SELECT DISTINCT(foo) is identical to SELECT DISTINCT foo. Including parens is entirely unnecessary, and makes it look like DISTINCT is a function (which it is not.)
You can try below query
SELECT DISTINCT ufs.card_type, ufs.split_type, COUNT(*) AS COUNT
FROM jg1_products p
LEFT JOIN jg1_product_types pt ON p.products_type = pt.type_id
LEFT JOIN jg1_products_to_categories ptc ON p.products_id = ptc.products_id
LEFT JOIN jg1_cards_ufs ufs ON ufs.products_id = p.products_id
WHERE type_handler LIKE "%product_cards%"
AND ptc.categories_id = 89
GROUP BY ufs.card_type
here query will select unique records from card_type and split_type
For more Link can help you.
try this:
SELECT COUNT(DISTINCT(ufs.rarity)) AS COUNT ...
Enjoy your code!

PHP/MySQL - Counting items within a query

I have the following results for my database table:
The Query:
SELECT
service_titles.user_id, service_titles.slide_id, service_titles.name as title_name ,service_names.name as service_name
FROM service_names
INNER JOIN service_titles ON service_names.title_id = service_titles.id
So what needs to happen is:
If the user has 2 unique service titles, then the max number of service_names for that title will be 6
If the user has 1 service title, the the max number of service_names for that title will be 16
I will be using PHP for all of the coding, but I am wondering how I would go about this. I need a way to count how many unique service_titles there are for that user and slide, and then count how many service items there are for each title.
Thanks for any help!
SELECT COUNT(DISTINCT service_titles.name)
FROM service_names
INNER JOIN service_titles ON service_names.title_id = service_titles.id
GROUP BY service_titles.user_id, service_titles.slide_id
That'll get you the number of distinct title_names for each user_id/slide_id combo.
SELECT COUNT(DISTINCT service_names.name)
FROM service_names
INNER JOIN service_titles ON service_names.title_id = service_titles.id
GROUP BY service_titles.user_id, service_titles.slide_id
... and that's the number of distinct service_names for same. If you want both in one query, you can put both COUNTs together, since you're using the same GROUP BY regardless.
You could use a CASE statement within your query to change the max number of service_names.
See MySQL CASE statement reference
To do this in the SQL itself would be quicker than evaluating it in PHP.
To count how many distinct titles you can try:
SELECT user_id, COUNT(DISTINCT name)
FROM service_titles
GROUP BY user_id

SQL order by, group by, having

I'm using a database to store results of an election with the columns id, candidate, post_time and result. Results are put in the database during 'counting the votes'. When a new update is available, a new entry will be inserted.
From this database, I would like to create a table with the most recent results (MAX post_time) per candidate (GROUP BY candidate), ordered by result (ORDER BY result).
How can I translate this to a working SQL-statement?
(I've tried mysql order and groupby without success)
I've tried:
SELECT *, MAX(time_post)
FROM [database]
GROUP BY candidate
HAVING MAX(time_post) = time_post
ORDER BY result
Assuming that you don't have multiple results per candidate at same time, next should work:
select r.candiate, r.result
from results r
inner join (
select candidate, max(post_time) as ptime
from results
group by candidate
) r2 on r2.candiate=r.candidate and r2.ptime=r.post_time
order by r.result
Note that MAX will not select the record with the maximum time, but it will select the maximum value from any record. So
SELECT MAX(a), MAX(b) FROM example
where exmple contains the two records a=1, b=2 and a=4, b=0, will result in a=4, b=2, which wasn't in the data. You should probably create a view with the latest votes only from each candidate, then query that. For performance, it may be sensible to use a materialized view.
Is the post_time likely to be the same for all the most recent results? Also does each candidate only appear once per post_time?
This could be achieved by just using a SELECT statement. Is there a reason you need the results in a new table?
If each candidate only appears once per post_time:
SELECT candidate, result
FROM table
WHERE post_time = (SELECT MAX(post_time) FROM table)
If you want to count how many times a candidate appears in the table for the last post_time:
SELECT candidate, count(result) as ResultCount
FROM table
WHERE post_time = (SELECT MAX(post_time) FROM table)
GROUP BY candidate
By what i see from ur attempts i'd think you should use this
SELECT MAX(post_time) FROM `table` GROUP BY candidate ORDER BY result
but the MAX statment only return a single value therefore i dont see why ORDER BY would be needed.
if you want multiple results try looking up the TOP statment
One way (tied results shown):
SELECT t.*
FROM tableX AS t
JOIN
( SELECT candidate
, MAX(time_post) AS time_post
FROM tableX
GROUP BY candidate
) AS m
ON (m.candidate, m.time_post) = (t.candidate, t.time_post)
ORDER BY t.result
and another one (no ties, only one row per candidate shown):
SELECT t.*
FROM
( SELECT DICTINCT candidate
FROM tableX
) AS d
JOIN
tableX AS t
ON t.PK = --- the Primary Key of the table, here
( SELECT ti.PK --- and here
FROM tableX AS ti
WHERE ti.candidate = d.candidate
ORDER ti.time_post DESC
LIMIT 1
)
ORDER BY t.result

Categories