Order by ignoring second criteria - mysql

I'm having problems with my query, basically what I'm trying to do here is to order first by
item_info.content_time desc
and then by
item_views.views desc
My intention is to get the most recent items (order by item_info.content_time desc), with the most views (item_views.views desc). In essence something like the stackoverflow main page, where you have the most recent with the most views (I really don't know if that's how they are doing it). The following query either orders the items by one criteria or the other (if I reverse the order by criteria). Here's my code:
SELECT item_info.item_id, item_info.profile_id, item_info.tittle, item_covers.reference, usuarios.username, item_views.views
FROM item_info
LEFT JOIN item_covers ON item_covers.cover_id = item_info.book_id
LEFT JOIN usuarios ON item_info.profile_id = usuarios.id
LEFT JOIN item_views ON item_views.id = item_info.book_id
WHERE item_info.content_time
BETWEEN UNIX_TIMESTAMP( CURDATE( ) + INTERVAL -1
DAY )
AND UNIX_TIMESTAMP( CURDATE( ) + INTERVAL 1
DAY )
order by item_info.content_time desc, item_views.views desc
Roughly something like...
Expected output:
content_time | views
17:00 500
13:00 300
11:00 100
10:00 50
Actual output:
content_time | views
17:00 500
16:00 10
15:00 30
14:00 50

If you want a combination of the two you should create a column consisting of the combined (possibly weighed by an extra factor) sum of both like DATEDIFF(content_time,NOW())+views. By using DATEDIFF you will make sure that content_time is a numeric type and can be added to views. By using DATEDIFF()with the arguments in the shown order you will be getting a negative number for any content_times in the past which will reduce the combined value with views accordingly.
So your query should end in something like this
..
...
ORDER BY DATEDIFF(viewsitem_info.content_time,NOW())+viewsitem_info.views desc
DATEDIFF() gives the difference in days. Maybe you want to be a bit more precise. In that case you might want to use TIMESTAMPDIFF(MINUTE,viewsitem_info.content_time,NOW()) instead.
#Aaron: You will not have to change your data structure at all. If necessary you should introduce some kind of weighing factor like
ORDER BY
(0.123* TIMESTAMPDIFF(MINUTE,viewsitem_info.content_time,NOW())
+ viewsitem_info.view) DESC

write Your query like below
SELECT item_info.item_id, item_info.profile_id, item_info.tittle, item_covers.reference,
usuarios.username, item_views.views FROM item_info
LEFT JOIN item_covers ON item_covers.cover_id = item_info.book_id
LEFT JOIN usuarios ON item_info.profile_id = usuarios.id
LEFT JOIN item_views ON item_views.id = item_info.book_id
WHERE item_info.content_time
BETWEEN UNIX_TIMESTAMP( CURDATE( ) + INTERVAL -1DAY )
AND UNIX_TIMESTAMP( CURDATE( ) + INTERVAL 1
DAY )
order by item_info.content_time,item_views.views desc
I think it'll work.

Related

condition in SELECT in mysql

I have a query that looks like this
SELECT customer, totalvolume
FROM orders
WHERE deliverydate BETWEEN '2020-01-01' AND CURDATE()
Is there any way to select totalvolume for specific date range and make it a separate column?
So for example, I already have totalvolume. I'd like to also add totalvolume for the previous month as a separate column (totalvolume where deliverydate BETWEEN '2020-08-01' AND '2020-08-31'). Is there a function for that?
Simply use 2 table copies:
SELECT t1.customer, t1.totalvolume, t2.totalvolume previousvolume
FROM orders t1
LEFT JOIN orders t2 ON t1.customer = t2.customer
AND t1.deliverydate = t2.deliverydate + INTERVAL 1 MONTH
WHERE t1.deliverydate BETWEEN '2020-08-01' AND '2020-08-31';
You can do it with case/when construct in your columns and just expand your WHERE clause. Sometimes I would do it by having a secondary #variables to simplify my clauses. Something like
SELECT
o.customer,
sum( case when o.deliveryDate < #beginOfMonth
then o.TotalVolume else 0 end ) PriorMonthVolume,
sum( case when o.deliveryDate >= #beginOfMonth
then o.TotalVolume else 0 end ) ThisMonthVolume,
sum( o.totalvolume ) TwoMonthsVolume
FROM
( select #myToday := date(curdate()),
#beginOfMonth := date_sub( #myToday, interval dayOfMonth( #myToday ) -1 day ),
#beginLastMonth := date_sub( #beginOfMonth, interval 1 month ) ) SqlVars,
orders o
WHERE
o.deliverydate >= #beginLastMonth
group by
o.customer
To start, the "from" clause of the query alias "SqlVars" will dynamically create 3 variables and return a single row for that set. With no JOIN condition, is always a 1:1 ratio for everything in the orders table. Nice thing, you don't have to pre-declare variables and the #variables are available for the query.
By querying for all records on or after the beginning of the LAST month, you get all records for both months in question. The sum( case/when ) can now use those variables as the demarcation point for the respective volume totals.
I know you mentioned this was a simplified query, but masking that might not be a perfect answer to what you need, but may help you look at it from a different querying perspective.

Mysql sort by time (hourly)

I am trying to figure out how to sort by time hourly with the following dataset.
hm,total_count,avg_count,max_count
-------------------------------------
'23:15','17','5.6667','9'
'23:45','19','3.1667','5'
'06:15','13','6.5000','9'
'05:15','22','4.4000','7'
'05:45','12','6.0000','11'
'04:15','22','4.4000','6'
'04:45','14','4.6667','7'
'03:45','24','2.4000','5'
'02:45','82','5.4667','13'
'01:45','98','6.1250','13'
'00:45','59','4.2143','11'
My query:
SELECT DATE_FORMAT(`dt`, '%H:%i') as hm, SUM(`counts`) AS total_count,
AVG(`counts`) AS avg_count, MAX(`counts`) AS max_count
FROM pax_load_distribution , plans
WHERE `pax_load_distribution`.`plan_id` = `plans`.`id` AND
`plans`.`dt` BETWEEN '2017-05-01' AND '2017-05-31'
GROUP BY hm
ORDER BY HOUR(hm) DESC, MINUTE(hm) ASC;
But as you can see, the query organize the 23 hours on top but not in a sequence from 23,00,01,02 etc. How can I make it such that the 23 hours are on top follow by 00,01 etc.
Try to use HOUR(dt + INTERVAL 1 HOUR) instead of HOUR(hm) DESC in your ORDER BY clause.
You can try : ORDER BY (HUOUR(hm) * 60 + MINUTE(hm)) ASC

Sql query too slow - looking for alternative way to write it

I have this mysql query that does what it needs to but it takes a really long time to load the content where as my other queries run perfectly fine and quick. Is there a better way to do it? I just want it to load faster. Here is my query:
SELECT
DISTINCT( # Without this I get too many results
CONCAT_WS(' ',
MONTHNAME(a.DateTimeViewed),
YEAR(a.DateTimeViewed)
)
) AS ViewedDate, # Displays as "January 2017" (example)
(
SELECT COUNT(b.ViewID)
FROM views b
WHERE
MONTH(b.DateTimeViewed) = MONTH(a.DateTimeViewed) AND
YEAR(b.DateTimeViewed) = YEAR(a.DateTimeViewed)
) as TotalViews
FROM views a
WHERE a.DateTimeViewed >= date_sub(now(), interval 6 month)
ORDER BY YEAR(a.DateTimeViewed) ASC, MONTH(a.DateTimeViewed) ASC
You are working too hard.
SELECT CONCAT_WS(' ', MONTHNAME(a.DateTimeViewed), YEAR(a.DateTimeViewed)
) AS ViewedDate,
COUNT(*) as TotalViews
FROM views a
WHERE a.DateTimeViewed >= date_sub(now(), interval 6 month)
ORDER BY YEAR(a.DateTimeViewed) ASC, MONTH(a.DateTimeViewed) ASC
Since you are backing up 6 months from this instant, you are getting the count for only part of the first month. Perhaps you want to change one line:
WHERE a.DateTimeViewed >= CONCAT(LEFT(CURDATE() - INTERVAL 6 MONTH, 7), '-01')
COUNT(*) is the usual way to count rows. COUNT(x) does the same, but adds on the effort to filter out rows with x IS NULL.

Mysql subtracting values using row selected from min timestamp, grouping by id

I've been at this for a few hours now to no avail, pulling my hair out.
Edit: Im wanting to calculate the difference between the overall_exp column by using the same data from 1 day ago to calculate the greatest 'gain' for each user
Currently I'm take a row, then select a row from 1 day ago based on the first rows timestamp then subtract the overall_exp column from the 2 rows and order by that result whilst grouping by user_id
SQL Fiddle: http://sqlfiddle.com/#!2/501c8
Here is what i currently have, however the logic is completely wrong so im pulling 0 results
SELECT rsn, ts.timestamp, #original_ts := SUBDATE( ts.timestamp, INTERVAL 1 DAY), ts.overall_exp, ts.overall_exp - previous.overall_exp AS gained_exp
FROM tracker AS ts
INNER JOIN (
SELECT user_id, MIN( TIMESTAMP ) , overall_exp
FROM tracker
WHERE TIMESTAMP >= #original_ts
GROUP BY user_id
) previous
ON ts.user_id = previous.user_id
JOIN users
ON ts.user_id = users.id
GROUP BY ts.user_id
ORDER BY gained_exp DESC
You can do this with a self-join:
select t.user_id, max(t.overall_exp - tprev.overall_exp)
from tracker t join
tracker tprev
on tprev.user_id = t.user_id and
date(tprev.timestamp) = date(SUBDATE(t.timestamp, INTERVAL 1 DAY))
group by t.user_id
A key here is converting the timestamps to dates, so the comparison is exact.
Try:
select u.*, max(t.`timestamp`)-min(t.`timestamp`) gain
from users u
left join tracker t
on u.id = t.user_id and
t.`timestamp` >= date_sub(date(now()), interval 1 day) and
t.`timestamp` < date_add(date(now()), interval 1 day)
group by u.id
order by gain desc
SQLFiddle here.

MySQL: Getting "busiest" or "most popular" hour from a datetime field?

Consider the following table which has the fields - id (int) and date_created (datetime):
id date_created
1 2010-02-25 12:25:32
2 2010-02-26 13:40:37
3 2010-03-01 12:02:22
4 2010-03-01 12:10:23
5 2010-03-02 10:10:09
6 2010-03-03 12:45:03
I want to know the busiest/most popular hour of the day for this set of data. In this example, the result I'm looking for would be 12.
Ideas?
To get just the most popular hour, use this query
select date_format( date_created, '%H' ) as `hour`
from [Table]
group by date_format( date_created, '%H' )
order by count(*) desc
limit 1;
If you want to look at all the data, go with this one
select count(*) as num_records
, date_created
, date_format( date_created, '%H' ) as `hour`
from [Table]
group by `hour`
order by num_records desc;
If you want something a little more flexible, perhaps to the half hour, or quarter hour, you can do the following:
SELECT floor(time_to_sec(date_created)/3600),count(*) AS period
FROM table GROUP BY period ORDER BY c DESC
If you want the most popular 2 hour interval, use 7200. The most popular 15 minute interval, use 900. You just need to remember you are dealing with seconds (3600 seconds in an hour).
Use the hour() function to extract the hour, then do the usual aggregation:
SELECT count(hour(date_created)) AS c, hour(date_created) AS h FROM table GROUP BY h ORDER BY c DESC;
I like both Simon and Peter's answers, but I can't select both as accepted. I combined the 2 to make a cleaner query that only returned the popular hour (I don't need the counts).
SELECT hour(date_created) AS h
FROM my_table
GROUP BY h
ORDER BY count(*) DESC
LIMIT 1
You could try this:
SELECT
DATE_FORMAT(date,'%H') as hours,
count(*) as count
FROM
myTable
GROUP BY
hours
ORDER BY
count DESC