SQL Min function on Date - sql

How does Min function work on dates ? If 2 records have the same date and time stamp , the min function returns 1. Does it pull records based on when it was put into the table ?

MIN is an aggregate function so it will return 1 record in your question's case. Since the two records have the same date and timestamp it doesn't matter which date and timestamp are returned (they're the same). Finally, the time the records were inserted is not considered.

MIN() returns the smallest of all selected values of a column. It seems to me that your statement may simply be asking if a minimum exists.
Please post your sql statement.
possibly this is what you need:
SELECT MIN (date) AS "Min Date"
FROM tablename;

Elliot already expained it.
Just a sidenode, if you are using MySQL: MySQL allows to aggregate on a certain column, while fetching other columns without aggregation. (SQL Server does NOT allow that!)
Example:
date | name
2015-03-06 | A
2015-03-06 | B
Using SELECT Min(date), name FROM table on MySQL will return various results.
Sometimes it will be
2015-03-06 | A
sometimes
2015-03-06 | B
Docu:
When using this feature, all rows in each group should have the same
values for the columns that are omitted from the GROUP BY part. The
server is free to return any value from the group, so the results are
indeterminate unless all values are the same.
SQL Server will throw an error, that no aggregation has been performed on column name. See also http://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html
MySQL works this way, cause sometimes grouping on the second column is not really required, for example:
SELECT MAX(id), user_id FROM posts WHERE user_id = 6
(There could be NO other user_id than 6, so aggregation is not required in MySQL - However not paying attention on THIS will lead to wrong results as example one shows.)

Related

getting the last occurence of a certain item in a sql request

I am trying to write sql requests to my database in order to get the last occurence of a certain item, here's what I mean illustrated by an example:
pigc=# SELECT date FROM msr_history WHERE pos = 'DV' AND msr_id = 177;
date
----------------------------
2018-06-20 08:04:09.724103
2018-06-20 08:09:49.484921
(2 rows)
The first line of my example is my sql request and I am trying to get only the last date, in this example it would be this one: 2018-06-20 08:09:49.484921
Note that the number of dates can differ so I can't just manualy select the second date each time. Thank you for your help.
Use select max(column) to retrieve the highest value for that specific data type for all rows included in the where clause.
In this instance, no aggregation is required since no non-aggregated columns are being retrieved.
See https://www.postgresql.org/docs/9.5/functions-aggregate for a complete list of aggregate functions and specifically https://www.postgresqltutorial.com/postgresql-max-function
The fastest way to achieve it to use the LIMIT clause in your query. This will prevent using aggregate function in your query and will run fastest -
SELECT date
FROM msr_history
WHERE pos = 'DV'
AND msr_id = 177
ORDER BY date DESC
LIMIT 1;

Using group by with date

I have a table containing the following columns:
stats_date (YYYY-MM-DD)
registered (INT)
opened_form (INT)
Compose a query that will return the total registered, and opened_form by month for the last 3 months. Also a calculated column called conversion_rate which is the registered column divided by the opened_form.
Are you just looking for aggregation? Date/time functions differ significantly among databases, but the idea is:
select year(stats_date), month(stats_date),
sum(registered), sum(opened_form),
sum(registered) * 1.0 / sum(opened_form) as ratio
from t
group by year(stats_date), month(stats_date)
order by min(stats_date);
Of course, your database might have a different way of extracting the year and month from a date.
You can see the ANSI SQL at page 187 to understand how agregation works. To know how to group your column by Month you need to check the documentation of your db, usually is MONTH(COLUMN_NAME).

Select all records from specified day

I have table named table_food in db with columns: first name, last name, date, food name. I want a query that returns all food names from specified date.
For example my table records looks like:
John Watson 2016-08-22 steak
John Watson 2016-08-22 burger
John Watson 2016-08-23 fries
John Watson 2016-08-23 apple
and I want to get all food names from 2016-08-23. How should I create my query?
I´m just assuming you´re using a MySQL-Database. The answer may vary for other databases.
There are two versions, depending on what you´re trying to get.
If you just want a list of all foods, including duplicates, you could use:
select food_name from table_food where date = '2016-08-23'
If you just need to get distinct values (each food name once) you could use:
select distinct(food_name) from table_food where date = '2016-08-23'
The first question could be: Which meals have been served and how many of them?
The second question could be: Which meals have been served at all (no matter how often)
It depends from the database you use.
I added also a distinct because I imagine that you need only distinct values of food names.
For MySql
select distinct(food_name) from table_food
where date = '2016-08-23'
For Oracle
select distinct(food_name) from table_food
where date = to_date('2016-08-23', 'YYYY-MM-DD')
Check for dialects of other databases.
Note that if the data stored in the date column has also hours, minutes and seconds you need a different query to extract data, for example in oracle:
select distinct(food_name) from table_food
where trunc(date) = to_date('2016-08-23', 'YYYY-MM-DD')
$date = "2016-08-23";
SELECT * FROM `table_food` WHERE `date` = '{$date}';
The reason I'd variable the date is solely down to as and when you wish to change the date. Don't get me wrong either of the above you can do. My personal preference would be to adjust the variable rather than the query.
When you need to retrieve data from a table, you'll have to specify what field you need to select from what table, under one or several conditions.
Since your condition is the date,
we'll use this syntax:
We added the word distinct in case you didn't need redundancy.
Select distinct FoodName
from Table_Food
Where date = '2016-08-23'

Greatest date from list of date

I have this simple data base:
7/2/2013
7/13/2013
I write a simple SQL statement to select the greatest date from a list of date. I try to use the (max function) as follow:
select max([P_Date]) from [BalDB].[dbo].[tab_Product]
The result was incorrect; it gives me the smallest date not the greatest as follow:
7/2/2013
So please help me to know what is the problem in my SQL statement and how can I solve it
Problem: Get the greatest date from a list of date or compare it with local date and take the greater!!
The sql max function returns the largest value of the selected column, in your case since your data type is a nvarchar the largest value is what is alphabetically larger, which in this case is 7/2/2013 (since the "2" is greater then the "1" in "13").
What you need to do is basically what #David mentioned, either chance the data type of the column or if it isn't feasible then you can cast it in your query as a datetime
For example
select max(cast([P_Date] as datetime)) from [BalDB].[dbo].[tab_Product]
The max function is making this slower than it needs to be, try this.
select top 1 convert(datetime,P_Date) from [BalDB].[dbo].[tab_Product] order by convert(datetime,P_Date) desc
Now your dates should be date types, not varchars, but for the sake of querying your data as is, this will work.

Best way in MySQL or Rails to get AVG per day within a specific date range

I'm trying to make a graph in Rails, for example the avg sales amount per day for each day in a given date range
Say I have a products_sold model which has a "sales_price" float attribute. But if a specific day has no sales (e.g none in the model/db), I want to return simply 0.
What's the best way in MySQL/Rails to get this done? I know I can do something like this:
(This SQL query might be the completely wrong way to get what I'm wanting too)
SELECT avg(sales_price) AS avg, DATE_FORMAT(created_at, '%m-%d-%Y') AS date
FROM products_sold WHERE merchant_id = 1 GROUP BY date;
And get results like this:
| avg | date |
23 01-03-2009
50 01-05-2009
34 01-07-2009
... ...
What I'd like to get is this:
| avg | date |
23 01-03-2009
0 01-04-2009
50 01-05-2009
0 01-06-2009
34 01-07-2009
0 01-08-2009
... ...
Can I do this with SQL or will I have to post-process the results to find what dates in the daterange aren't in the SQL result set? Perhaps I need some sub-selects or IF statements?
Thanks for any help everyone.
Is there a reason (other than the date one already mentioned) why you wouldn't use the built-in group function capabilities in ActiveRecord? You seem to be concerned about "post-processing", which I don't think is really something to worry about.
You're in Rails, so you should probably be looking for a Rails solution first[1]. My first thought would be to do something like
Product.average(:sales_price, :group => "DATE(created_at)", :conditions => ["merchant_id=?", 1])
which ActiveRecord turned into pretty much the SQL you described. Assuming there's a declared has_many association between Merchant and Product, then you'd probably be better using that, so something like:
ave_prices = Merchant.find(1).products.average(:sales_price, :group => "DATE(created_at)")
(I'm hoping that your description of the model as "products_sold" is some kind of transcription error, btw - if not, you're somewhat off-message with your class naming!)
After all that, you're back where you started, but you got there in a more conventional Rails way (and Rails really values conventions!). Now we need to fill in the gaps.
I'll assume you know your date range, let's say it's defined as all dates from from_date to to_date.
date_aves = (from_date..to_date).map{|dt| [dt, 0]}
That builds the complete list of dates as an array. We don't need the dates where we got an average:
ave_price_dates = ave_prices.collect{|ave_price| ave_price[0]} # build an array of dates
date_aves.delete_if { |dt| ave_price.dates.index(dt[0]) } # remove zero entries for dates retrieved from DB
date_aves.concat(ave_prices) # add the query results
date_aves.sort_by{|ave| ave[0] } # sort by date
That lot looks a bit cluttered to me: I think it could be terser and cleaner. I'd investigate building a Hash or Struct rather than staying in arrays.
[1] I'm not saying don't use SQL - situations do occur where ActiveRecord can't generate the most efficient query and you fall back on find_by_sql. That's fine, it's supposed to be like that, but I think you should try to use it only as a last resort.
For any such query, you will need to find a mechanism to generate a table with one row for each date that you want to report on. Then you will do an outer join of that table with the data table you are analyzing. You may also have to play with NVL or COALESCE to convert nulls into zeroes.
The hard part is working out how to generate the (temporary) table that contains the list of dates for the range you need to analyze. That is DBMS-specific.
Your idea of mapping date/time values to a single date is spot on, though. You'd need to pull a similar trick - mapping all the dates to an ISO 8601 date format like 2009-W01 for week 01 - if you wanted to analyze weekly sales.
Also, you would do better to map your DATE format to 2009-01-08 notation because then you can sort in date order using a plain character sort.
To dry up a bit:
ave_prices = Merchant.find(1).products.average(:sales_price, :group => "DATE(created_at)")
date_aves = (from_date..to_date).map{|dt| [dt, ave_prices[dt.strftime "%Y-%m-%d"] || 0]}
Does MySQL have set-returning functions? I.e. functions that return different values on each row of a query? As an example from PostgreSQL, you can do:
select 'foo', generate_series(3, 5);
This will produce a result set consisting of 2 columns and 3 rows, where the left column contains 'foo' on each row and the right column contains 3, 4 and 5.
So, assuming you have an equivalent of generate_series() in MySQL, and subqueries: What you need is a LEFT OUTER JOIN from this function to the query that you already have. That will ensure you see each date appear in the output:
SELECT
avg(sales_price) as avg,
DATE_FORMAT(the_date, '%m-%d-%Y') as date
FROM (select cast('2008-JAN-01' as date) + generate_series(0, 364) as the_date) date_range
LEFT OUTER JOIN products_sold on (the_date = created_at)
WHERE merchant_id = 1
GROUP BY date;
You may need to fiddle with this a bit to get the syntax right for MySQL.