MySQL - Use min function or limit an order-by query - mysql

I am writing a query to find the product with the minimum price.
These are the two queries I tried:
select min(price) from products
and
select price from products order by price limit 1
The first one returns 19.950000762939453 and the second one returns 19.95 which is the accurate value. So my question is, what's the difference of the two queries?, why is the first one weird?! and which has a better performance for this task?
Thanks in advance.

Your data type of price is probably a floating-point with is by definition inaccurate.
If you use a fixed-point data type like decimal it will be 19.95.
You can read it up in the doc

min has better performance, according strange values - you should read how floating numbers are stored in memory/db, they are "rounded"
if you store real price - go with DECIMAL type, it will work fine

Related

Limit in the number of aggregated characters after running GROUP_CONCAT in SQL

I am using GROUP_CONCAT in my query to collect some IDs. Imagine I have two columns as follow:
user_id
item
The code looks like this:
SELECT user_id, GROUP_CONCAT(item) AS all_items_by_user
FROM table
GROUP BY user_id
However, I noticed that the all_items_by_user column can accommodate up to a certain number of characters in each cell. This limit is around 1100 in my case, whereas I expect at least 10 times more. I wondered whether there is any way to increase this limit?
Thank you.
This is controlled by group_concat_max_len, a system variable.
The documentation describes how to change the value and what it really means.

how to display the value of avg function till only two decimal places in SQL?

select avg(purch_amt) from orders;
I took an average of the amounts and it shows value till many decimal places and I would like to limit it to two decimal places.
Here's what I have tried:-
select cast(avg(purch_amt) as decimal(10,2)) from orders;
please answer containing both the operations(first average and then conversion). I would like to know the correct syntax of writing both the statements together.
You can update your query to:
select ROUND (avg(purch_amt),2) as Average from orders;
You can use ROUND as
SELECT ROUND(AVG(purch_amt), 2) AS 'Average' from orders
or if you want to Round all column values before calculating average
SELECT AVG(ROUND(purch_amt, 2)) AS 'Average' from orders
My preferred method is to cast to an appropriate decimal value:
select cast(avg(purch_amt) as decimal(10, 2))
from orders;
This is a bit different from round(), although the two generally produce the same result. As the documentation explains for round():
The return value has the same type as the first argument (assuming
that it is integer, double, or decimal)
That means that what gets displayed in an application might still show more (or fewer) than the number of decimal places. By changing the type, the application will know how many decimal places should be in the result.
You can see from this example that round() does not change the data type -- hence it might not change the representation in a tool.

Rails ActiveRecord "maximum(:column)" ignores order

I am trying to retrieve the maximum value of a column using ActiveRecord, but after I order and limit the values.
My query is:
max_value = current_user.books.order('created_at DESC').limit(365).maximum(:price)
Yet the resulting query is:
(243.0ms) SELECT MAX(`books`.`price`) AS max_id FROM `books` WHERE `books`.`user_id` = 2 LIMIT 365
The order is ignored completely and as a result the maximum value comes from the first 365 records instead of the last 365 records.
There's a curious line in the active record code (active_record/relation/calculations.rb) which removes the ordering. I say curious because it refers specifically to postgres:
# Postgresql doesn't like ORDER BY when there are no GROUP BY
relation = reorder(nil)
You should be able to use pluck to achieve what you want. It can select a single attribute which can be a reference to an aggregate function:
q = current_user.books.order('created_at DESC').limit(365)
max_value = q.pluck("max(price)").first
pluck will return an array of values so you need the first to get the first one (and only one in this case). If there are no results then it will return nil.
According to the rails guides maximum returns the maximum value of your table for this field so I suppose Active Records tries to optimize your query and ends up messing up with the order of executing your chained methods.
Could you try: First query the 365 rows you want, and then get the maximum?
max_value = (current_user.books.order('created_at DESC').limit(365)).maximum(:price)
I have found the solution thanks to #RubyOnRails on freenode:
max_value = current_user.books.order('created_at DESC').limit(365).pluck(:price).max
Of course the drawback is that this will grab all 365 prices and calculate the max locally. But I'll survive.
Best and the most effective way is to do subquery .. do something like this ...
current_user.books.where(id: current_user.books.order('created_at DESC').limit(365)).maximum(:price)

Inexact searching for nearest time value

I'm looking for a programmatically and syntactically efficient way to go about searching for a value based on a Timestamp. Essentially I want to find the closest timestamp match...
Say for example in a MySQL table I have:
ID TIME Blob
1 4:03:10 abc
2 4:04:30 def
3 4:04:45 ghi
And I want to query this table based on the time 4:04:40. I would want to return record ID #3... If I searched for 4:04:35 I would want to return ID #2... How do I go about implementing this? I have many millions of rows in this table and I was thinking something like levenshtein dist will be too slow..?
Cheers!
How about:
SELECT id, MIN(ABS(time(time) - time("4:04:35"))) FROM table
Bear with me, I'm more used to Oracle where dates can be manipulated exactly like numbers if necessary, but I think you should look for the row that has the date with the smallest difference to your search date. The difference could be positive or negative so ABS() will be needed to take care of that.
How about
SELECT table.id,
ABS(TIMESTAMPDIFF(SECOND, table.time, [datetime.now])) as difference
FROM table
If that looks OK you then have to select the ID that has the minimum [difference]
SELECT MIN(time) FROM table WHERE TIME > [datetime.now]

MySql Query Grouping by Time

I am trying to create a report to understand the time-of-day that orders are being placed, so I need to sum and group them by time. For example, I would like a sum of all orders placed between 1 and 1:59, then the next row listing the sum of all orders between 2:00 and 2:59, etc. The field is a datetime variable, but for the life me I haven't been able to find the right query to do this. Any suggestions sending me down the right path would be greatly appreciated.
Thanks
If by luck it is mysql and by sum of orders you mean the number of orders and not the value amount:
select date_format(date_field, '%Y-%m-%d %H') as the_hour, count(*)
from my_table
group by the_hour
order by the_hour
This king of grouping (using a calculated field) will certainly not scale over time. If you really need to execute this specific GROUP BY/ORDER BY frequently, you should create an extra field (an UNSIGNED TINYINT field will suffice) storing the hour and place an INDEX on that column.
That is of course if your table is becoming quite big, if it is small (which cannot be stated in mere number of records because it is actually a matter of server configuration and capabilities as well) you won't probably notice much difference in performance.