Distinct in complex sql query - php

I have another one for you guys. I've searched around but I can't figure out the cause of this issue.
Basically, I want to do a DISTINCT on user_id, which is a column being naturally joined and exists in the tables "user" and "leaderboard_entry".
This is my original query which works great, but I'd like to filter out duplicates and show only the first fastest user score. The inner query basically grabs 100 of the most recent rows, and the outer query resorts them by ascending leaderboard_entry_elapsed_time_ms.
set #t1=0; select * from
(
select #t1 := #t1+1 as leaderboard_entry_youngness_rank, 1-#t1/100 as
leaderboard_entry_youngness_based_on_expiry,
leaderboard_entry.*,
NOW()-leaderboard_entry_timestamp as leaderboard_entry_age_in_some_units ,
TO_DAYS(NOW())-TO_DAYS(leaderboard_entry_timestamp)
as leaderboard_entry_age_in_days , leaderboard.leaderboard_quiz_mode ,
leaderboard.leaderboard_load_key ,
user.user_name
from leaderboard_entry
natural join
leaderboard
natural join
user
where
(leaderboard_load_key = 'es-en-animals-1'
or leaderboard_load_key = '-es-en-animals-1' )
and leaderboard_quiz_mode = '0'
order by leaderboard_entry_age_in_some_units asc ,
leaderboard_entry_timestamp asc limit 0, 100
) as outer_temp
order by
leaderboard_entry_elapsed_time_ms asc ,
leaderboard_entry_timestamp asc
limit 0, 50
I've tried the following, which is removing "leaderboard_entry.*, " and adding the DISTINCT keyword like so with explicit naming of the columns I need:
set #t1=0; select * from
(
select #t1 := #t1+1 as leaderboard_entry_youngness_rank, 1-#t1/100 as
leaderboard_entry_youngness_based_on_expiry,
NOW()-leaderboard_entry_timestamp as leaderboard_entry_age_in_some_units ,
TO_DAYS(NOW())-TO_DAYS(leaderboard_entry_timestamp)
as leaderboard_entry_age_in_days , leaderboard.leaderboard_quiz_mode ,
leaderboard.leaderboard_load_key ,
user.user_name
distinct leaderboard_entry.user_id,
leaderboard_entry.leaderboard_entry_id,
leaderboard_entry.leaderboard_id,
leaderboard_entry.leaderboard_entry_timestamp,
leaderboard_entry.leaderboard_entry_elapsed_time_ms,
from leaderboard_entry
natural join
leaderboard
natural join
user
where
(leaderboard_load_key = 'es-en-animals-1'
or leaderboard_load_key = '-es-en-animals-1' )
and leaderboard_quiz_mode = '0'
order by leaderboard_entry_age_in_some_units asc ,
leaderboard_entry_timestamp asc limit 0, 100
) as outer_temp
order by
leaderboard_entry_elapsed_time_ms asc ,
leaderboard_entry_timestamp asc
limit 0, 50
But i get this error and it makes no sense... :(
#1064 - You have an error in your SQL syntax;
check the manual that corresponds to your MySQL
server version for the right syntax to use near
'distinct leaderboard_entry.user_id, leaderboard_entry.leaderboard_entry_id, '
at line 12
Any help much appreciated!
swine

Answered by The Scrum Meister as above

Related

New version of php/mysql got an aggregate error in GROUP BY clause

I've got an error on my code saying "Invalid in the select list because it is not contained/ aggregate in the group by clause" like that. This error seems familiar to me in "MS SQL Server". I haven't encountered this error before in MySQL. This error came up when I upgraded my Php version to 7. All my previous web-based program before were affected
I tried to add more column in my "group by" clause, the error got away but the output is not what I am expected. The code below is my old code.
SELECT SUM(s.pscore) as towtal, s.pscore AS totalScore, s.cri_id,
c.can_id, c.canid,c.can_name FROM score s INNER JOIN candidate c ON
s.can_id = c.can_id WHERE cat_id=1 AND s.cri_id = '".$rows['cri_id']."'
AND c.can_sex = 'Female' AND c.can_id='".$kert[$i]."'
GROUP BY s.can_id ORDER BY s.can_id ASC LIMIT 5
When I add GROUP BY s.can_id, s.pscore there will be no errors, but the output is not what I am expected
go to your database and in sql run this command
set GLOBAL sql_mode='';
SET sql_mode=(SELECT REPLACE(##sql_mode, 'ONLY_FULL_GROUP_BY', ''));
An example of a valid query:
SELECT SUM(s.pscore) as towtal
, s.pscore AS totalScore
, s.cri_id
, c.can_id
, c.canid
, c.can_name
FROM score s
JOIN candidate c
ON s.can_id = c.can_id
WHERE cat_id = 1
AND s.cri_id = '".$rows['cri_id']."'
AND c.can_sex = 'Female'
AND c.can_id = '".$kert[$i]."'
GROUP
BY s.pscore
, s.cri_id
, c.can_id
, c.canid
, c.can_name
ORDER
BY s.can_id ASC
LIMIT 5;
Note that this query is vulnerable to injection

Need to perform ORDER BY twice, problems with ranking

I have been struggling to set this up for months and months!
I need help with setting rank to my database.
This is how my current code looks like:
$db->queryNoReturn("SET #a:=0");
return $db->query("
SELECT * FROM
(SELECT
`FFA_Stats`.`id`,
`FFA_Stats`.`player_uuid`,
`FFA_Stats`.`points`,
`FFA_Stats`.`hits`,
`FFA_Stats`.`shots`,
`FFA_Stats`.`wins`,
`FFA_Stats`.`tkills`,
`FFA_Stats`.`tdeaths`,
(`FFA_Stats`.`tkills`/`FFA_Stats`.`tdeaths`) as `KDR`,
`player`.`name`,
`player`.`uuid`,
`player`.`online`,
(#a:=#a+1) AS rank
FROM `FFA_Stats`
INNER JOIN `player` ON `FFA_Stats`.`player_uuid`=`player`.`uuid`
ORDER BY `points` DESC
) AS `sub`
");
Basically its sorting it by points and you can check how it looks like here: http://filipvlaisavljevic.com/clash/ffa.php
All I want to do is add rank to the sorted table so the player with the most points would be #1 etc.
Does anyone know what to do?
Usually a rank number would be an integer that you could generate from iterating through the rows of the query result. eg. echo $count++;
If you have calculated or attributed a rank in your database then you can add 'order by' statements separated by commas. eg.
FROM `FFA_Stats`
INNER JOIN `player`
ON `FFA_Stats`.`player_uuid`=`player`.`uuid`
ORDER BY `rank` DESC, `points` DESC) AS `sub`
");

mysql union statement failing because of bracket placement?

I have written three working sql statement, and am trying to use a union like this:
(SELECT * FROM articles WHERE date <= '2015-01-15' AND category='News' )
UNION
(SELECT * FROM articles WHERE date <= '2015-01-15' AND subcategory='News')
ORDER BY date DESC LIMIT 5
UNION
SELECT * FROM articles WHERE Month(recurring) = '01' AND category='News' ORDER BY
recurring DESC LIMIT 5
The basic structure is what I'm asking about: two initial queries unioned and sorted together by date.
Then a third query, sorted by a separate column, and then unioned into the first two.
The query above seems to work fine when I sort the first 2 queries inside the brackets separately, but fails once I take it out of the brackets and sort them together.
Is sorting by a separate column causing it to fail? I tried adding an additional set of brackets to encapsulate the first 2 statements plus the sort order, but no go.
Here is the error code:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'UNION SELECT * FROM articles WHERE Month(recurring) = '01' AND category='News' at line 2
There are a few problems with your query. An UNION query can only have one LIMIT clause and one ORDER BY clause, and both are applied to the whole result.
If I understand your logic correctly, I would rewrite your query as this:
(
SELECT * FROM articles
WHERE date <= '2015-01-15' AND (category='News' OR subcategory='News')
ORDER BY date DESC
LIMIT 5
)
UNION
(
SELECT * FROM articles
WHERE Month(recurring) = '01' AND category='News'
ORDER BY recurring DESC
LIMIT 5
)
if your query is more complicated and you really want your first two queries to be separated, this is the right syntax to use:
SELECT * FROM (
SELECT * FROM articles WHERE date <= '2015-01-15' AND category='News'
UNION
SELECT * FROM articles WHERE date <= '2015-01-15' AND subcategory='News'
ORDER BY date DESC
LIMIT 5
) AS s
UNION
(
SELECT * FROM articles
WHERE Month(recurring) = '01' AND category='News'
ORDER BY recurring DESC
LIMIT 5
)

How To Find the Average of Differences of The Last N Readings of a Column PHP MySQL

I have a table in a MySQL database (level_records) which has 3 columns (id, date, reading). I want to put the differences between the most recent 20 readings (by date) into an array and then average them, to find the average difference.
I have looked everywhere, but no one seems to have a scenario quite like mine.
I will be very grateful for any help. Thanks.
SELECT AVG(difference)
FROM (
SELECT #next_reading - reading AS difference, #next_reading := reading
FROM (SELECT reading
FROM level_records
ORDER BY date DESC
LIMIT 20) AS recent20
CROSS JOIN (SELECT #next_reading := NULL) AS var
) AS recent_diffs
DEMO
If we consider "differences" to be signed, and if we ignore/exclude any rows that have a NULL values of reading...
If you want to return just the values of the difference between a reading and the immediately preceding reading (to get the latest nineteen differences), then you could do something like this:
SELECT d.diff
FROM ( SELECT e.reading - #prev_reading AS diff
, #prev_reading AS prev_reading
, #prev_reading := e.reading AS reading
FROM ( SELECT r.date
, r.reading
FROM level_records r
CROSS
JOIN (SELECT #prev_reading := NULL) p
ORDER BY r.date DESC
LIMIT 20
) e
ORDER BY e.date ASC
) d
That'll get you the rows returned from MySQL and you can monkey with them in PHP however you want. (The question of how to monkey around with arrays in PHP is a question that doesn't really have anything to do with MySQL.)
If you want to know how to return rows from a SQL resultset into a PHP array, that doesn't really have anything to do with "latest twenty", "difference", or "average" at all. You'd use the same pattern you'd use for returning the result from any query. There's nothing at all unique about that, there are plenty of examples of that, (most of them unfortunately using the deprecated mysql_ interface; for new development, you want to use either PDO or mysqli_.
If you mean by "all 19 sets of differences" that you want to get the difference between a reading and every other reading, and do that for each reading, such that you get a total of 380 rows ( = 20 * (20-1) rows ) then:
SELECT a.reading - b.reading AS diff
, a.id AS a_id
, a.date AS a_date
, a.reading AS a_reading
, b.id AS b_id
, b.date AS b_date
, b.reading AS b_reading
FROM ( SELECT aa.id
, aa.date
, aa.reading
FROM level_record aa
WHERE aa.reading IS NOT NULL
ORDER BY aa.date DESC, aa.id DESC
LIMIT 20
) a
JOIN ( SELECT bb.id
, bb.date
, bb.reading
FROM level_record bb
WHERE bb.reading IS NOT NULL
ORDER BY bb.date DESC, bb.id DESC
LIMIT 20
) b
WHERE a.id <> b.id
ORDER BY a.date DESC, b.date DESC
Sometimes, we only want differences in one direction, that is, if we have the difference between r13 and r15, we essentially already have the inverse, the difference between r15 and f13. And sometimes, it's more convenient to have the inverse copies.
What query you run really depends on what result set you want returned.
If the goal is to get an "average", then rather than monkeying with PHP arrays, we know that the average of the differences between the latest twenty readings will be the same as the difference between the first and last readings (in the latest twenty), divided by nineteen.
If we only want to return a row if there are at least twenty readings available, then something like this:
SELECT (l.reading - f.reading)/19 AS avg_difference
FROM ( SELECT ll.reading
FROM level_records ll
WHERE ll.reading IS NOT NULL
ORDER BY ll.date DESC LIMIT 1
) l
CROSS
JOIN (SELECT ff.reading
FROM level_records ff
WHERE ff.reading IS NOT NULL
ORDER BY ff.date DESC LIMIT 19,1
) f
NOTE: That query will only return a row only if there are at least twenty rows with non-NULL values of reading in the level_records table.
For the more general case, if there are fewer than twenty rows in the table (i.e. fewer than nineteen differences) and we want an average of the differences between the latest available rows, we can do something like this:
SELECT (l.reading - f.reading)/f.cnt AS avg_difference
FROM ( SELECT ll.reading
FROM level_records ll
WHERE ll.reading IS NOT NULL
ORDER BY ll.date DESC
LIMIT 1
) l
CROSS
JOIN (SELECT ee.reading
, ee.cnt
FROM ( SELECT e.date
, e.reading
, (#i := #i + 1) AS cnt
FROM level_records e
CROSS
JOIN (SELECT #i := -1) i
WHERE e.reading IS NOT NULL
ORDER BY e.date DESC
LIMIT 20
) ee
ORDER BY ee.date ASC
LIMIT 1
) f
But, if we need to treat "differences" as unsigned (that is, we are taking the absolute value of the differences between the readings),
then we'd need to get the actual differences between the readings, and then average the absolute values of the differences...
then we could do make use of a MySQL user variable to keep track of the "previous" reading, and have that available when we process the next row, so we can get the difference between them, something like this:
SELECT AVG(d.abs_diff)
FROM ( SELECT ABS(e.reading - #prev_reading) AS abs_diff
, #prev_reading AS prev_reading
, #prev_reading := e.reading AS reading
FROM ( SELECT r.date
, r.reading
FROM level_records r
CROSS
JOIN (SELECT #prev_reading := NULL) p
ORDER BY r.date DESC
LIMIT 20
) e
ORDER BY e.date ASC
) d

MySQL Error in Nested Query

I have the following code in PHP:
SELECT (
SELECT (
SELECT `forum_posts.id`,`forum_posts.author`,`forum_posts.author_id`, `forum_boards.date`, MIN(`forum_posts.date`) FROM `forum_posts`
WHERE `parent` IN
(SELECT `id` FROM `forum_boards` WHERE `parent`="'.Oflow(intval((isset($_GET['id']) ? $_GET['id'] : '0'))).'")
INNER JOIN `forum_boards`
ON `forum_boards.id`=`forum_posts.id`
ORDER BY `update_date` DESC
LIMIT 1
GROUP BY `parent`;
) ORDER BY `order_large`,`order`;
) UNION (
SELECT `name`,`id`,`info`,`parent_name` FROM `forum_boards` WHERE `parent`="'.Oflow(intval((isset($_GET['id']) ? $_GET['id'] : '0'))).'" ORDER BY `order_large`,`order
)
This is a script to get a list of boards and posts in a forum system. What it's supposed to do is get data from a table "boards" and "posts". It then tries to find the most recent post in the sub-board of the board currently being viewed. Then, it tries to join the "parent" and board "id" togeather, in order that the columns can be matched and the posts be sorted accordingly. Finally, a UNION is performed to combine the newly found and ordered posts with the actual forum data.
The error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INNER JOIN forum_boards ON forum_boards.id=forum_posts.id ORDER BY' at line 7
The problem is, it don't work! I've double checked every column and table name, and they all exist. This is a very "alpha" code, so if you have any efficiency tips that's be great.
You can't INNER JOIN in the WHERE clause which is what your query is trying to do
SELECT (
SELECT (
SELECT `forum_posts.id`,`forum_posts.author`,`forum_posts.author_id`, `forum_boards.date`, MIN(`forum_posts.date`) FROM `forum_posts`
WHERE `parent` IN
(SELECT `id` FROM `forum_boards` WHERE `parent`="'.Oflow(intval((isset($_GET['id']) ? $_GET['id'] : '0'))).'")
INNER JOIN `forum_boards` ## This is effectively JOINing within a WHERE clause
ON `forum_boards.id`=`forum_posts.id`
ORDER BY `update_date` DESC
LIMIT 1
GROUP BY `parent`;
) ORDER BY `order_large`,`order`;
) UNION (
SELECT `name`,`id`,`info`,`parent_name` FROM `forum_boards` WHERE `parent`="'.Oflow(intval((isset($_GET['id']) ? $_GET['id'] : '0'))).'" ORDER BY `order_large`,`order
)
You might need to rethink your query a little bit.

Categories