storing dates in mysql - mysql

Is it better to store dates in mysql in three columns or use just one column. Which one is faster. Also, if I just want to do inserts with todays date in format dd/mm/yy , how do I do that. and also how do I do selects with that. Also, lets say if I wanted to get results for all the wednesdays, how do I do that or lets say one date 25th of all the months and years, how do i do that.
Thanks People.
I am using PHP with Apache and Mysql.
What are the drawbacks of using the structure that I am proposing. I can easily get all the 25th by using the date table and I can get all the days using another column for days. How much difference would be there in the terms of speed between my proposed solution and using a DATE table?

You will want to use a proper column type, such as DATE, DATETIME, or TIMESTAMP, depending on your needs. They are built specifically to handle dates, and can more easily perform other functions (adding, comparing, etc.) that would be difficult to perform on 3 separate columns.
Read this for more info.
DAYOFWEEK(date) will give you a numeric representation for the day. In your case, 4 = Wednesday. DAYOFMONTH(date) will work for finding all 25th days of the month.
DAYNAME(date) will return the name of the weekday for date

Also, if I just want to do inserts with todays date in format dd/mm/yy ,how do I do that.
Well it depends on the format your date is passed in through your
form but you are going to want to store your date in YYYY-mm-dd format.
INSERT INTO my_table (timefieldname) VALUES ( '$date' );
and also how do I do selects with that.
SELECT timefieldname FROM my_table;
//or you can format the date - this will give you month/day/year 01/01/2012
SELECT DATE_FORMAT(timefieldname, '%m/%d/%Y') FROM my_table;
Also, lets say if I wanted to get results for all the wednesdays,
SELECT timefieldname FROM my_table WHERE DAYNAME(timefieldname) = 'Wednesday';
How do I do that or lets say one date 25th of all the months and years, how do i do that.
SELECT timefieldname FROM my_table WHERE DAY(timefieldname) = '25';

You can free up having to pass dates from your codebase and let mysql insert them for you, provided they are time stamps:
ALTER TABLE tablename ADD `timefieldname` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ;
It's not much of a speed boost, but it does reduce your need to code and validate variables passed to the database.

Related

SQL SELECT - order dates with wrong format

I was tasked with ordering some entries in our web application. Current solution made by some other guy 10 years ago, is that there is a select on db and then it iterates and make table.
Problem is, that date is in dd-mm-yyyy format and in varchar data.
And not really sure, if I am brave enought to make changes to the database.
So is there some way to order it anyway within a select, some way to order it by the end meaby? Or only way without making some gruesome function in code is to change the db?
You can use the STR_TO_DATE() function for this. Try
ORDER BY STR_TO_DATE(varcharDateColumn, '%d-%m-%Y')
It converts your character-string dates to the DATE datatype where ordering works without trouble.
As of MySQL 5.7 or later, you can add a so-called generated column to your table without touching the other data.
ALTER TABLE tbl
ADD COLUMN goodDate
AS (STR_TO_DATE(varcharDateColumn, '%m-%d-%Y'))
STORED;
You can even put an index on that column if you need to use it for searrching.
ALTER TABLE t1 ADD INDEX goodDate(goodDate);
You can use STR_TO_DATE function, but this will work only for small tables(maybe thousands of records), on large data sets you will face performance problems:
SELECT *
FROM (
SELECT '01-5-2013' AS Date
UNION ALL
SELECT '02-6-2013' AS Date
UNION ALL
SELECT '01-6-2013' AS Date
) AS t1
ORDER BY STR_TO_DATE(Date,'%d-%m-%Y')
Long term solution should be conversion of that column to proper date type.

fetch rows from table based on from and to dates

I have a table as shown here:
In the table there are two columns named DateFrom and DateTo.
I want to fetch row data from or between these two dates. e.g. If I want to fetch data from 2018-12-27 to 2019-01-10 it should return the two rows with HPID 1 and 6 - as both row have or comes under the mentioned dates.
Now I don't know what the SQL query should be. I have tried BETWEEN but with no result.
Actually I am working on a small hotel management system and the table shows the cost of any hotel between certain dates. So when the user searches for hotels for date between two dates it should show costs which overlap those dates.
DateFrom and DateTo Are not datetime.
Stefan Taseki's answer is almost right but doesn't deal with overlaps correctly. Swap the start and end dates round in that query and it should do the job:
SELECT *
FROM [YourTable]
WHERE DateFrom <= '2019-01-10' AND DateTo >= '2018-12-27'
P.S. You should always store your dates in datetime columns. Dates are not text. You will have issues with a) sorting, b) comparisons, and c) presenting dates in different formats if you don't do this. The datetime data type exists for good reasons, you should use it. Consider changing your database now while you can.
P.P.S. Your data is also de-normalised. Country, City, HotelName, HotelCode and HotelStar should all be in a separate "hotel" table (and country and city each should be in different tables too), and then with a foreign key on Hotel ID only in this costs table. Otherwise you keep repeating data which should only be entered once. I suggest you learn about relational database design and normalisation, if you didn't realise this.
This should work:
SELECT *
FROM [YourTable]
WHERE DateFrom >= '2018-12-27' AND DateTo<= 2019-01-10
First of all make sure that your dates are in single quotes
'SELECT * FROM your_table WHERE DateFrom BETWEEN '2018-12-27' and '2019-01-10' OR DateTo BETWEEN '2018-12-27' and '2019-01-10';
Something that is not clear is what are you trying to achieve with this query.
Casting may be required, use below query
select *from your_table_name
where date(DateFrom) >= date('2018-11-27') AND date(DateTo) <= date('2019-01-10')

Large mysql query with group by

I have a pricing history table with half a billion records. It is formatted like this:
Id, sku, vendor, price, datetime
What I want to do is get average price of all products by vendor for a certain date range. Most products are updated once every 3 days, but it varies.
So, this is the query I want to run:
SELECT
avg(price)
FROM table
WHERE
vendor='acme'
AND datetime > '12-15-2014'
AND datetime < '12-18-2014'
GROUP BY sku
This 3 day range is broad enough that i will for sure get at least one price sample, but some skus may have been sampled more than once, hence group by to try and get only one instance of each sku.
The problem is, this query runs and runs and doesn't seem to finish (more than 15 minutes). There are around 500k unique skus.
Any ideas?
edit: corrected asin to sku
For this query to be optimized by mysql you need to create a composite index
(vendor, datetime, asin)
IN THIS PARTICULAR ORDER (it mattters)
It also worth trying creating another one
(vendor, datetime, asin, price)
since it may perform better (since it's a so called "covering index").
The indexes with other order, like (datetime, vendor) (which is suggested in another answer) are useless since the datetime is used in a range comparison.
Few notes:
The index will be helpful if only the vendor='acme' AND datetime > '12-15-2014' AND datetime < '12-18-2014' filter condition covers a small part of the whole table (say less than 10%)
Mysql does not support dd-mm-yyyy literals (at least it's not documented, see references) so I assume it must be yyyy-mm-dd instead
Your comparison does not cover the first second of the December 15th, 2014. So you probably wanted datetime >= '2014-12-15' instead.
References:
http://dev.mysql.com/doc/refman/5.6/en/range-optimization.html
http://dev.mysql.com/doc/refman/5.6/en/date-and-time-literals.html
You need an index to support your query. Suggest you create an index on vendor and datetime like so:
CREATE INDEX pricing_history_date_vendor ON pricing_history (datetime, vendor);
Also, I assume you wanted to group by sku rather than undefined column asin.
Not to mention your non-standard SQL date format MM-dd-yyyy as pointed out by others in comments (should be yyyy-MM-dd).

Age calculation - Fastest age calculation formula from timestamp field

I'm using this query to calc an user age:
SELECT
((YEAR(CURDATE())-YEAR(um.date_birth)) - (RIGHT(CURDATE(),5)<RIGHT(um.date_birth,5)))
FROM user_meta AS um
WHERE um.uid = 2
I wonder if there's a fastest / lighter way to do it.
I'm using this nested in a bigger select, so I wouldn't mind to somehow enhance the overall performance. Thank you.
select (CURDATE()-birth_dt)/365 from dual
If you find that you need to calculate a field too often, I would suggest you ALTER your table to add new columns to facilite your calculations.
For example, rather than calculate the YEAR for each date of birth in each query, simple add a YEAR column, update the table ONCE with the year of birth, and store the year as an INT.
That way, every time you need to catch the year, just select the field.

MySQL Select Dates in the Past: Problem with MAX and GROUP BY

So, the query I'm trying to make looks like this right now:
SELECT `jid`, MAX(`start`) as `start` FROM `dates` WHERE `start` < NOW() GROUP BY `jid`
The table dates basically holds a DATETIME value start associated with a job.
As you probably guessed, jid is the id of a job, stored in another table.
A job can have many dates
What I'm trying to accomplish is to find out what jobs have all their start dates in the past.
My thought was to select the highest values of the start dates, grouped by jid and if they were in the past, it would imply that all other dates associated with the same job are also in the past.
This query isn't working because it's checking for start dates in the past and then selecting the highest of those. This doesn't exclude the possibility of another date for the same job, lying in the future.
I have no idea how I could proceed now. Any help or clue is appreciated.
Cheers - Tom
You have to use HAVING :
SELECT jid, MAX(start) as start
FROM dates
GROUP BY jid
HAVING MAX(start) < NOW();
HAVING acts a bit like WHERE. It filters out the rows after they were selected. Usually (and actually, I can't think of any other case), you only use HAVING with aggregate functions.
(I hope you really inserted dates in the future in your table, though!)