i'm build an exercises web app and i'm working with two tables like this:
Table 1: weekly_stats
| id | code | type | date | time |
|----|--------------|--------------------|------------|----------|
| 1 | CC | 1 | 2015-02-04 | 19:15:00 |
| 2 | CC | 2 | 2015-01-28 | 19:15:00 |
| 3 | CPC | 1 | 2015-01-26 | 19:15:00 |
| 4 | CPC | 1 | 2015-01-25 | 19:15:00 |
| 5 | CP | 1 | 2015-01-24 | 19:15:00 |
| 6 | CC | 1 | 2015-01-23 | 19:15:00 |
| .. | ... | ... | ... | ... |
Table 2: global_stats
| id | exercise_number |correct | wrong |
|----|-----------------|--------|-----------|
| 1 | 138 | 1 | 0 |
| 2 | 246 | 1 | 0 |
| 3 | 988 | 1 | 10 |
| 4 | 13 | 5 | 0 |
| 5 | 5 | 4 | 7 |
| 6 | 5 | 4 | 7 |
| .. | ... | ... | ... |
What i would like is to get MAX(correct-wrong) and MIN(correct-wrong) and now i'm working with this query:
SELECT
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MAX(correct - wrong) from global_stats)
UNION
SELECT
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MIN(correct - wrong) from global_stats);
This query is working good, except for one thing: when "WHERE correct - wrong = (SELECT MIN(correct - wrong)[...]" selects more than one row, the row selected is the first but i would like to have returned the most recent (in other words: ordered by datetime(date, time)). Is it possible?
Thanks!
I think you can solve it like this:
SELECT * FROM (
SELECT
1 as sort_column,
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MAX(correct - wrong) from global_stats)
ORDER BY date DESC, time DESC
LIMIT 1 ) as a
UNION
SELECT * FROM (
SELECT
2 as sort_column,
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MIN(correct - wrong) from global_stats)
ORDER BY date DESC, time DESC
LIMIT 1) as b
ORDER BY sort_column;
Here is the documentation about how UNION works.
Related
I have two tables in my mysql database:
1) Videos
+----+--------+----------+------+----------+
| id | title | category | year | director |
+----+--------+----------+------+----------+
| 1 | Title1 | Cat1 | 2021 | A.K. |
| 2 | Title2 | Cat2 | 2020 | B.C. |
| 3 | Title3 | Cat3 | 2000 | E.A. |
+----+--------+----------+------+----------+
2) Videos_insights
+----------+------------+-------+-------+----------+--------+
| video_id | date | views | likes | dislikes | shares |
+----------+------------+-------+-------+----------+--------+
| 1 | 2021-03-20 | 13 | 2 | 3 | 1 |
| 1 | 2021-03-19 | 35 | 1 | 3 | 3 |
| 1 | 2021-03-18 | 68 | 5 | 6 | 5 |
| 1 | 2021-03-15 | 86 | 3 | 0 | 1 |
| 2 | 2021-02-13 | 234 | 15 | 1 | 34 |
| 2 | 2021-02-12 | 55 | 15 | 2 | 4 |
| 2 | 2021-02-10 | 331 | 255 | 0 | 0 |
+----------+------------+-------+-------+----------+--------+
And I want to get videos that had between watched 2021-03-01 to 2021-03-31. So the result table should look like this:
+--------+-------------------------------------------+
| title | date_range |
+--------+-------------------------------------------+
| Title1 | ["2021-03-20 - 2021-03-18", "2021-03-15"] |
+--------+-------------------------------------------+
In my MySQL database, I have about 100 000 videos and each video has about 100 video_insight.
What is the best way to achieve the result table?
How to optimize? I mean I do not want to make every GET request left join? It would take too long and my server will burn out.
I would express the dates individually:
select v.id, v.title,
group_concat(date) as dates
from videos v join
video_insights vi
on vi.video_id = v.id
where vi.date >= '2021-03-01' and
vi.date < '2021-04-01'
group by v.id;
Note that a left join is not appropriate because you are filtering the values.
If you really want to get the ranges, then you can use window functions with a gaps-and-islands approach:
select v.id, v.title, group_concat(date_range)
from videos v join
(select vi.video_id,
concat_ws(' - ', min(vi.date), nullif(max(date), min(date))) as date_range
from (select vi.*,
dense_rank() over (partition by vi.video_id order by vi.date) as seqnum
from video_insights vi
where vi.date >= '2021-03-01' and
vi.date < '2021-04-01'
) vi
group by vi.video_id, date - interval seqnum day
) vi
on vi.video_id = v.id
group by v.id;
I have data something like this:
| visitor_id | time | my_flag |
| 1 | 01-01-2017 | 1 |
| 2 | 01-01-2017 | 1 |
| 1 | 01-02-2017 | 2 |
| 3 | 01-02-2017 | 2 |
etc...
I want to group all visitors in one day but based on a flag. The flag can have two values, 1 or 2. So I need something like
| 12 | 01-01-2017 | 1 |
| 10 | 01-01-2017 | 2 |
| 34 | 01-02-2017 | 1 |
I have
select v.visitor_id v.time, v.flag
from my_table as v
group by DAY(v.time) having flag=1;
But I am not able to figure out how to add multiple conditions (flag=1 and flag=2)? Any pointers?
try something like this
select v.visitor_id v.time, v.flag
from my_table as v
where v.flag in (1,2)
group by DAY(v.time), v.flag
SELECT v.time,
v.flag,
COUNT(v.visitor_id)
FROM my_table as v
GROUP BY v.time,
v.flag;`
I have 2 tables
Transaction table
+----+----------+-----+---------+----
| TID | CampaignID | DATE |
+----+----------+-----+---------+---+
| 1 | 5 | 2016-01-01 |
| 2 | 5 | 2016-01-01 |
| 3 | 2 | 2016-01-01 |
| 4 | 5 | 2016-01-01 |
| 5 | 1 | 2016-01-01 |
| 6 | 1 | 2016-02-02 |
| 7 | 3 | 2016-02-02 |
| 8 | 3 | 2016-02-02 |
| 9 | 5 | 2016-02-02 |
| 10| 4 | 2016-02-02 |
+----+----------+-----+---------+---+
Campaign Table
+-------------+----------------+--------------------
| CampaignID | DailyMaxImpressions | CampaignActive
+-------------+----------------+--------------------
| 1 | 5 | Y |
| 2 | 5 | Y |
| 3 | 5 | Y |
| 4 | 5 | Y |
| 5 | 1 | Y |
+-------------+----------------+--------------------
What I am trying to do is get a single random campaign where the the count in transaction table is less than the daily max impressions in the campaign table. I might also be passing a date s part of the query for the transaction table
So for CampaignId 1 there must be 4 trans of less in the transaction table and the Campaignactive must be a "Y"
Any help would be appreciated if this can be done in a single statement. ( mysql )
Thanks in advance,
Jeff Godstein
This should get it for you. The basic query is select each campaign that is active. The INNER query will pre-aggregate per campaign for the given date in question. From that, a LEFT-JOIN allows any campaign to be returned even if it does NOT exist within the subquery OR it DOES exist, but the count is less than that allowed for the date in question. The order by RAND() is obvious.
SELECT
c.CampaignID
from
Campaign c
LEFT JOIN
( select
t1.CampaignID,
count(*) as CampCount
from
Transaction t1
where
t1.Date = YourDateParameterValue
group by
t1.CampaignID ) as T
ON c.CampaignID = T.CampaignID
where
c.CampaignActive = 'Y'
AND ( t.CampaignID IS NULL
OR t.CampCount < c.DailyMaxImpressions )
order by
RAND()
I am designing a simple architecture where i have a table which stores users and some elements that they like so my table structure is something like this:
+---------+---------+
| user_id | like_id |
+---------+---------+
| 1 | 4 |
| 2 | 2 |
| 4 | 4 |
| 4 | 3 |
| 5 | 4 |
| 6 | 7 |
| 7 | 5 |
| 34 | 6 |
| 3 | 8 |
| 2 | 3 |
| 2 | 5 |
| 1 | 3 |
| 1 | 10 |
| 1 | 12 |
| 2 | 10 |
+---------+---------+
Now what i will have is id of any user (lets say user_id = 1 ) and i want a query to get all the other users who have similar Likes as that of 1.
So in the Output for user_id = 1 will be :
+---------------------------+------------------------+----------------+
| users_with_common_likes | no_of_common_likes | common_likes |
+---------------------------+------------------------+----------------+
| 4 | 2 | 3,4 |
| 2 | 2 | 3,10 |
| 5 | 1 | 4 |
+---------------------------+------------------------+----------------+
What I have achieved :
I can do this using a sub-query as below :
SELECT user_id
FROM `user_likes`
WHERE `like_id`
IN (
SELECT GROUP_CONCAT( `like_id` )
FROM user_likes
WHERE user_id =1
)
AND user_id !=1
LIMIT 0 , 30
However this query is not giving all the users,it misses the user_id = 2 which has like id 3 in common with user_id=1.
and i cant figure out how to find the remaining 2 columns.
Also I feel that this is not the best way to to this as this table will contain thousands of data and it may effect system performance.
I would like to do this with a single Mysql Query.
This assumes a PK formed on user_id,like_id...
SELECT y.user_id
, GROUP_CONCAT(y.like_id) likes
, COUNT(*) total
FROM my_table x
JOIN my_table y
ON y.like_id = x.like_id
AND y.user_id <> x.user_id
WHERE x.user_id = 1
GROUP
BY y.user_id;
Given a *students_exam_rooms* table:
+------------+---------+---------+
| student_id | room_id | seat_no |
+------------+---------+---------+
| 1 | 30 | 1001 |
| 2 | 30 | 1002 |
| 3 | 31 | 2001 |
| 4 | 32 | 2002 |
| 5 | 33 | 3001 |
| 6 | 33 | 3002 |
| 7 | 34 | 4001 |
| 8 | 34 | 4002 |
+------------+---------+---------+
And *students_tbl*:
+------------+-------------+------+
| student_id | studen_name | year |
+------------+-------------+------+
| 1 | Eric | 1 |
| 2 | Mustafa | 1 |
| 3 | Michael | 2 |
| 4 | Andy | 2 |
| 5 | Rafael | 3 |
| 6 | Mark | 3 |
| 7 | Jack | 4 |
| 8 | peter | 4 |
+------------+-------------+------+
How can I select from *students_exam_rooms* ordering by *students_tbl.year* but with one after one like this:
+--------------+------+
| student_name | year |
+--------------+------+
| Eric | 1 |
| Michael | 2 |
| Rafael | 3 |
| Jack | 4 |
| Mustafa | 1 |
| Andy | 2 |
| Mark | 3 |
| Peter | 4 |
+--------------+------+
I'm assuming that you want to order by the "occurrence-count" of the year then the year, e.g. all the first-occurrences of all years first, sorted by year, then all second-occurrences of all years also sorted by year, and so on. That would be a perfect case for emulating other RDBMS' analytic / windowing functions:
select *
from (
select
s.studen_name,
s.year,
ser.*,
(
select 1 + count(*)
from students_tbl s2
where s.year = s2.year
and s.student_id > s2.student_id
) rank
from students_tbl s
JOIN students_exam_rooms ser
ON s.student_id = ser.student_id
) i_dont_really_want_to_name_this
order by rank, year
Here it is against a slightly tweaked version of JW's fiddle: http://www.sqlfiddle.com/#!2/27c91/1
Emulating Analytic (AKA Ranking) Functions with MySQL is a good article that gives more background and explanation.
try any of these below:
SELECT a.studen_name, a.year
FROM students_tbl a
INNER JOIN students_exam_rooms b
ON a.student_id = b.student_id
ORDER BY REVERSE(b.seat_no),
a.year
SQLFiddle Demo
by using Modulo
SELECT a.studen_name, a.year
FROM students_tbl a
INNER JOIN students_exam_rooms b
ON a.student_id = b.student_id
ORDER BY CASE WHEN MOD(b.seat_no, 2) <> 0 THEN 0 ELSE 1 END,
a.year
SQLFiddle Demo
Looks to me like you're trying to sort first by seat and then by year. Looking at your students_exam_rooms table, it looks like you started with a simple seat number and prepended year * 1000. So, if we omit the year, it looks like this:
> select * from fixed_students_exam_rooms;
+------------+---------+---------+
| student_id | room_id | seat_no |
+------------+---------+---------+
| 1 | 30 | 1 |
| 2 | 30 | 2 |
| 3 | 31 | 1 |
| 4 | 32 | 2 |
| 5 | 33 | 1 |
| 6 | 33 | 2 |
| 7 | 34 | 1 |
| 8 | 34 | 2 |
+------------+---------+---------+
And if you had that table, your query is simple:
select
student_name, year
from
modified_student_exame_rooms
left join students_tbl using (student_id)
order by
seat_no, year
;
Using the table as you currently have it, it's only slightly more complicated, assuming the "core seat number" doesn't excede 999.
select
student_name, year
from
modified_student_exame_rooms
left join students_tbl using (student_id)
order by
convert(substr(seat_no, 2), unsigned),
year
;