MySQL Subquery SUM Limit - mysql

I'm trying to do an subquery with SUM() and LIMIT. This works fine with the following code:
SELECT id,
(
SELECT SUM(number)
FROM (
SELECT number
FROM t2
WHERE u_id = '1'
ORDER BY time ASC
LIMIT 30
) AS temp
) AS test
FROM t1
But I want to do it of course dynamically and with the current row ID.
I changed the Query to the following:
SELECT id,
(
SELECT SUM(number)
FROM (
SELECT number
FROM t2
WHERE u_id = p.id
ORDER BY time ASC
LIMIT 30
) AS temp
) AS test
FROM t1 p
This will give the following error:
Unknown column 'p.id' in 'where clause'
Any ideas how to make it working?

Unfortunately, MySQL limits the scope of table aliases. Oracle is another database that does this.
You can phrase your query as a complicated join:
select t1.id, sum(t2.number)
from t1 p join
t2
on p.id = t2.u_id
where 30 >= (select count(*)
from t2 t22
where t22.u_id = t2.u_id and
t22.time <= t2.time
)
group by t1.id;
Or you can do this with variables:
select p.id, sum(number)
from t1 p join
(select t2.*,
#rn := if(#u = t2.u_id, #rn + 1, if((#u := t2.u_id) is not null, 1, 0)) as rn
from t2
(select #u := 0, #rn := 0) vars
order by t2.u_d, time
) t2
on p.id = t2.u_id
where rn <= 30
group by p.id;

why not just change p.id to t1.id? I'm pretty sure it's because you are aliasing t1 in the first select, and it isn't defined in the subquery. Try an inner join instead.
SELECT id,
(
SELECT SUM(number)
FROM (
SELECT number
FROM t2
INNER JOIN t1 p
on u_id = p.id
ORDER BY time ASC
LIMIT 30
) AS temp
) AS test
FROM t1 p

Try this:
SELECT id, temp2.sum_number as test
FROM t1 p
INNER JOIN
(
SELECT SUM(number) as sum_number, temp.u_id
FROM (
SELECT number, u_id
FROM t2
WHERE u_id = p.id
ORDER BY time ASC
LIMIT 30
) AS temp
) AS temp2 ON temp2.u_id = p.id
I moved subqueries in the join part, so i can access to p.id in the subquery.

Related

How to get the maximum and minimum values in each group as well as their times?

Let's say I have a table MyTable with the columns Id, NumericValue and UTCTimestamp. I'm trying to group the results of my table MyTable by the hour of their timestamp and return the maximum NumericValuefor each group with its associated timestamp as well as the minimum NumericValue for each group with its associated timestamp value.
For now, I'm able to achieve the first part of my problem with the following query:
SELECT
HOUR(t.UTCTimestamp) AS `Hour`,
t.NumericValue AS MaximumValue,
t.UTCTimestamp AS MaximumValueTime
FROM MyTable t
INNER JOIN (
SELECT HOUR(t2.UTCTimestamp) AS `Hour`, MAX(t2.NumericValue) AS NumericValue
FROM MyTable t2
GROUP BY HOUR(t2.UTCTimestamp)
) maxNumericValue ON HOUR(t.UTCTimestamp) = maxNumericValue.`Hour` AND t.NumericValue = maxNumericValue.NumericValue
GROUP BY HOUR(t.UTCTimestamp);
Which was inspired by this answer.
Here's an MVCE.
How could I also show the minimum value for each group as well as the timestamp associated to it?
Starting from MySQL 8.0 you could use ROW_NUMBER:
WITH cte AS (
SELECT *,ROW_NUMBER() OVER(PARTITION BY HOUR(UTCTimestamp)
ORDER BY UTCTimestamp ASC) AS rn
,ROW_NUMBER() OVER(PARTITION BY HOUR(UTCTimestamp)
ORDER BY UTCTimestamp DESC) AS rn2
FROM MyTable
)
SELECT HOUR(c1.UTCTimestamp),
c1.ID, c1.NumericValue, c1.UTCTimestamp, -- min row
c2.ID, c2.NumericValue, c2.UTCTimestamp -- max row
FROM cte c1
JOIN cte c2
ON HOUR(c1.UTCTimestamp) = HOUR(c2.UTCTimestamp)
AND c1.rn=1
AND c2.rn2=1
ORDER BY HOUR(c1.UTCTimestamp) ASC;
DBFiddle Demo
You can join to MyTable twice (and only use one aggregating subquery)
SELECT bounds.`Hour`
, minT.NumericValue AS MinValue
, minT.UTCTimestamp AS MinTime
, maxT.NumericValue AS MaximumValue
, maxT.UTCTimestamp AS MaximumValueTime
FROM (
SELECT HOUR(t2.UTCTimestamp) AS `Hour`
, MAX(t2.NumericValue) AS maxValue
, MIN(t2.NumericValue) AS minValue
FROM MyTable t2
GROUP BY HOUR(t2.UTCTimestamp)
) bounds
LEFT JOIN MyTable minT ON bounds.`Hour` = HOUR(minT.UTCTimestamp)
AND bounds.minValue = minT.NumericValue
LEFT JOIN MyTable maxT ON bounds.`Hour` = HOUR(maxT.UTCTimestamp)
AND bounds.maxValue = maxT.NumericValue
;
Apply the same technique but with minimum:
select a.*, b.MinimumValueTime from (
SELECT
HOUR(t.UTCTimestamp) AS `Hour`,
t.NumericValue AS MaximumValue,
t.UTCTimestamp AS MaximumValueTime
FROM MyTable t
INNER JOIN (
SELECT HOUR(t2.UTCTimestamp) AS `Hour`, MAX(t2.NumericValue) AS NumericValue
FROM MyTable t2
GROUP BY HOUR(t2.UTCTimestamp)
) maxNumericValue ON HOUR(t.UTCTimestamp) = maxNumericValue.`Hour` AND t.NumericValue = maxNumericValue.NumericValue
GROUP BY HOUR(t.UTCTimestamp))a
join
(
SELECT
HOUR(t.UTCTimestamp) AS `Hour`,
t.NumericValue AS MinimumValue,
t.UTCTimestamp AS MinimumValueTime
FROM MyTable t
INNER JOIN (
SELECT HOUR(t2.UTCTimestamp) AS `Hour`, MIN(t2.NumericValue) AS NumericValue
FROM MyTable t2
GROUP BY HOUR(t2.UTCTimestamp)
) minNumericValue ON HOUR(t.UTCTimestamp) = minNumericValue.`Hour` AND t.NumericValue = minNumericValue.NumericValue
GROUP BY HOUR(t.UTCTimestamp))b on a.hour=b.hour

Group by and get the latest row from each group

I want to return only the newest rows that have a different video_id.
I have been having trouble getting this to work no matter which way I try it... I have done this before and it isn't that difficult but for some reason my particular query will not work.
My table/results:
I have been trying this:
SELECT * FROM user_video_history
WHERE `user_id` = $db_safe_user_id
GROUP BY video_id
ORDER BY `date` DESC LIMIT 3
I have also tried this:
SELECT *
FROM (SELECT *
FROM user_video_history
WHERE `user_id` = $db_safe_user_id
ORDER BY `date` DESC)
GROUP BY `video_id`
ORDER BY `date` DESC
You cannot use select * when you GROUP BY
Try this...
SELECT
A . *
FROM
user_video_history A,
(SELECT
video_id, max(date) maxdate
FROM
user_video_history
WHERE
`user_id` = $db_safe_user_id
GROUP BY video_id) B
where
A.video_id = B.video_id
and A.date = B.maxdate
order by A.date DESC
limit 3
Try this:
SELECT
t1 . *
FROM
user_video_history t1
JOIN
(SELECT
id, MAX(`date`) `date`
FROM
user_video_history
GROUP BY user_id) t2 ON t1.user_id = t2.user_id
AND t1.`date` = t2.`date`
LIMIT 3

Why do I receive an error at this join tables?

I wrote this:
SELECT DISTINCT CATEGORY FROM T AS T1
CROSS JOIN (SELECT *
FROM T
WHERE T.CATEGORY = T1.CATEGORY
ORDER BY CATEGORY DESC
LIMIT 10)
and I receive this
"Unknown column 'T1.CATEGORY' in 'where clause'".
Why?
Update:
My purpose of this is to get 10 posts of any category.
Because T1 is not visible from within the subquery.
Your JOIN also serves no purpose and/or you probably forgot the JOIN condition.
In JOIN condition should use ON keyword
SELECT DISTINCT CATEGORY FROM T AS T1
CROSS JOIN SELECT * FROM T ON T.CATEGORY = T1.CATEGORY
ORDER BY CATEGORY DESC LIMIT 10;
If you need to get 10 posts of each category you can use a query like this:
SELECT CATEGORY, Post
FROM (
SELECT a.CATEGORY, a.Post, count(*) as rn
FROM #T a
JOIN #T b ON a.CATEGORY = b.CATEGORY AND a.Post >= b.Post
GROUP BY a.CATEGORY, a.Post) dt
WHERE rn < 11;

MySQL reference outer table alias in subquery error

select c.*
from posts p
join comments c on (p.id=c.post_id)
and c.status = 'A'
and c.id >= (select MIN(id) from (select id from comments where post_id=p.id order by id DESC LIMIT 7) as c2)
where p.comments_count > 0 AND p.id IN (247,245,244,243,242,241)
In the above sql query I get this mysql error Unknown column 'p.id' in 'where clause' it seems like in the subquery p.id isn't able to get referenced. Is there anyway that I can get the posts table id referenced in this subquery?
I think you are trying to fetch latest 7 comments for each post. Could you try this? you can test here http://www.sqlfiddle.com/#!2/a222e/3/0
The First Attempt
I tried below SQL.
SELECT *
FROM comments t1
WHERE post_id IN (247,254,244,243,242,241)
AND id IN (
SELECT id
FROM comments
WHERE t1.id = id
LIMIT 7
);
But I got an error "This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery"
Another Approach
So, I tried self join on comments to generate sequence number.
SELECT id
FROM (
SELECT t1.id, COUNT(*) AS cnt
FROM comments t1 INNER JOIN comments t2
ON t1.post_id = t2.post_id
WHERE t1.id <= t2.id
AND t1.post_id IN (247,254,244,243,242,241)
AND t2.post_id IN (247,254,244,243,242,241)
GROUP BY t1.id
) x
WHERE cnt <= 7;
inner sub-query uses self join and produces cnt column which has sequential value for each comment id of post.
But preceding query only fetches id of comment
Finally to get all columns of comment table, following query should be executed.
SELECT *
FROM comments c INNER JOIN (
SELECT id
FROM (
SELECT t1.id, COUNT(*) AS cnt
FROM comments t1 INNER JOIN comments t2
ON t1.post_id = t2.post_id
WHERE t1.id <= t2.id
AND t1.post_id IN (247,254,244,243,242,241)
AND t2.post_id IN (247,254,244,243,242,241)
GROUP BY t1.id
) x
WHERE cnt <= 7
) t USING (id);
Using User Variables
Actually you have another chance using MySQL user variable. I didn't mention this interesting MySQL feature because I was not sure I understood your question correctly.
SELECT *
FROM (
SELECT post_id, id,
IF (#pid = post_id, #cnt := #cnt + 1, #cnt := 1) AS cnt,
#pid := post_id
FROM comments, (SELECT #pid := 0, #cnt := 0) tmp
WHERE post_id IN (247,254,244,243,242,241)
ORDER BY post_id, id DESC
) x
WHERE cnt <= 7;
Preceding SQL looks like simpler (means good performance) than older join version. but not tested on large data set.
You can only access the outer query 1 level deep. So try doing you where filter on the middle query:
SELECT MIN(id)
FROM (
SELECT id, post_id
FROM comments
ORDER BY id DESC
LIMIT 7
)
WHERE post_id = p.id

Return rows around row with a certain value with joins

I have fairly complicated join query from which I want to select a few rows around a result with a certain id.
The query currently looks something like this:
WITH results AS
(
SELECT t1.id, t1.position, t1.points, t2.name
ROW_NUMBER() OVER(ORDER BY t1.position ASC, t1.points DESC) AS rn
FROM Table1 t1
JOIN Table2 t2 ON t1.id = t2.Table1id
/* Several more joins here, some of which limit the result set */
)
SELECT * FROM results
WHERE rn < ( SELECT rn+3 FROM results WHERE id = #someid )
AND rn > ( SELECT rn-3 FROM results WHERE id = #someid )
Is there a better way to solve this? Most of all I'm worried about performance with these multiple calls to a possibly huge CTE.
The query is run on a SQL 2008 server.
Maybe pull the joins out of the CTE.
That way the query optimizer has a chance filter out rows before processing the joins.
WITH results AS
(
SELECT t1.id, t1.position, t1.points
, ROW_NUMBER() OVER(ORDER BY t1.position ASC, t1.points DESC) AS rn
FROM Table1 t1
)
SELECT results.id, results.position, results.points, t2.name
FROM results
JOIN Table2 t2 ON t2.id = results.Table1id
/* Several more joins here */
WHERE rn < ( SELECT rn+3 FROM results WHERE id = #someid )
AND rn > ( SELECT rn-3 FROM results WHERE id = #someid )
You could use another cte to help form the filter:
WITH results AS (
SELECT
t1.id
, t1.position
, t1.points
, t2.name
, ROW_NUMBER() OVER (ORDER BY t1.POSITION ASC, t1.points DESC) AS rn
FROM Table1 t1
JOIN Table2 t2
ON t1.id = t2.Table1id
/* Several more joins here, some of which limit the result set */
),
filter AS (
SELECT
rn
FROM results
WHERE id = #someid
)
SELECT
*
FROM results
WHERE rn < ( SELECT rn + 3 FROM filter )
AND rn > ( SELECT rn - 3 FROM filter )