I want to get the date of birth of a cat as a range of years.
The year range is as follows, and several selections are possible.
Year : [2000, 2010, 2020]
If I select 2020, the period from 2020-01-01 to 2029-12-31.
If I select 2000, 2020, the period from 2000-01-01 to 2009-12-31 and 2020-01-01 to 2029-12-31.
<TABLE>
CAT {
ID number,
Birth DateTime,
...
}
I have searched for various ways through books and Google, but I can't find the way I want to do so..
select * from CAT
where birth between '2000-01-01' and '2009-12-31'
or birth between '2010-01-01' and '2019-12-31'
or birth between '2020-01-01' and '2029-12-31'
I tried to use 'Between' or '-01-01', but if [2000, 2020] is selected, it must be connected with 'or'.
The more or, the slower the speed.
Please tell me a good way to do range calculations being able to use index.
The index is being used for BirthDate.
Add) In my db, the query of 'SUBSTRING(YEAR(CAT.birth),1,3) IN (200,202)' works quickly.
I have 500,000 data, can I use it like this?
All you need to do is add index to column birth and run your query above with BETWEEN and OR.
if you are using mysql, did you tried with YEAR() function ?
Example:
SELECT * FROM cat WHERE YEAR(birth) BETWEEN 1990 AND 2018 ORDER BY YEAR(birth) ASC;
Please check this Mysql YEAR()
If you expect to get more than about 20% of the rows from a table, then an INDEX will be eschewed for simply scanning all the rows.
Otherwise, having INDEX(birth) will help with certain queries, but none of the ones mentioned so far. Each of them is not "sargable" .
To use the index (and be efficient for a limited range of years or date range), something like this is probably what you need. This example covers 2 calendar years.
WHERE CAT.birth >= '2018-01-01'
AND CAT.birth < '2020-01-01'
BTW: SUBSTRING(YEAR(CAT.birth),1,3) can be simplified to LEFT(CAT.birth, 3), but that still cannot use the recommended index.
BTW: A 'bug' in your code: Since birth is a DATETIME, and '2009-12-31' excludes but midnight of the morning of New Year's Eve. Note how I avoided this common 'bug' by using < and the next day. This works whether you have DATE, DATETIME or DATETIME(6), etc.
Related
This is a question from leetcode, using the second query I got the question wrong but could not identify why
SELECT
user_id,
max(time_stamp) as "last_stamp"
from
logins
where
year(time_stamp) = '2020'
group by
user_id
and
select
user_id,
max(time_stamp) as "last_stamp"
from
logins
where
time_stamp between '2020-01-01' and '2020-12-31'
group by
user_id
The first query uses a function on every row to extract the year (an integer) and compares that to a string. (It would be preferable to use an integer instead.) Whilst this may be sub-optimal, this query would accurately locate all rows that fall into the year 2020.
The second query could fail to locate all rows that fall into 2020. Here it is important to remember that days have a 24 hour duration, and that each day starts at midnight and concludes at midnight 24 hours later. That is; a day does have a start point (midnight) and an end-point (midnight+24 hours).
However a single date used in SQL code cannot be both the start-point and the end-point of the same day, so every date in SQL represents only the start-point. Also note here, that between does NOT magically change the second given date into "the end of that day" - it simply cannot (and does not) do that.
So, when you use time_stamp between '2020-01-01' and '2020-12-31' you need to think of it as meaning "from the start of 2020-01-01 up to and including the start of 2020-12-31". Hence, this excludes the 24 hours duration of 2020-12-31.
The safest way to deal with this is to NOT use between at all, instead write just a few characters more code which will be accurate regardless of the time precision used by any date/datetime/timestamp column:
where
time_stamp >= '2020-01-01' and time_stamp <'2021-01-01'
with the second date being "the start-point of the next day"
See answer to SQL "between" not inclusive
I have a sql file containing bYear and u_age column in users table.
I would like to know how I can change all the digits in bYear, such as 1986, 2000, to u_age such as 33, 19.
Thanks so much !!
If you are looking to update the table (not the file), you can just do:
update users set u_age = year(curdate()) - bYear;
curdate() gives you the current date, from which you can extract the year using the year() function.
Please note that this computation is not accurate at all: to compute an age, you need the entire date of birth (including month and day). The above computation behaves like the date of birth is actually the first day of year bYear.
If you are looking to update a sql file: as commented by Raymond Nijland, just don't. This is much more complicated and far less efficient. Instead, load the file in a table, update the table and then export it to a file
I want to get the previous month date for specific dates in SQL. For example: 6.21.19 has a previous month date of 5.21.19.
I am just trying to get comps from this.
MONTH( curdate() ) -1
I need to return the previous month date.
Welcome to the board Arie. Judging from your question and responses, you need a range of dates and their prior month relations. The easiest way would be for all of the dates you need to look up to be in a table, then the answers provided so far would work. Since that doesn't appear to be the case, I'm guessing you are creating date ranges on the fly.
So lets assume you need exactly the data shown in your example, there are two parts to this, first you need to get a list of days that you want to look up, then you need to get the day in the prior month. There are lots of ways to get a sequence of days, but for simplicity I'll use a recursive CTE. Once I have the date range, I'll just select the dates and their prior month date as well.
with Date_CTE as (select cast('6/1/2019' as datetime) as repDate
union all
select dateadd(day, 1, repdate) as repDate
from Date_CTE
where repDate < '06/07/2019')
select repDate, dateadd(month, -1, repDate) as PriorDate
from Date_CTE
CTEs are helpful functions and you can get more details on them here, but it's worth noting there are many ways to do this. Hope this gets you pointed in the right direction.
SELECT yourDateColumn, yourDateColumn-interval 1 month as prevMonthDate
If I have MySQL query like this, summing word frequencies per week:
SELECT
SUM(`city`),
SUM(`officers`),
SUM(`uk`),
SUM(`wednesday`),
DATE_FORMAT(`dateTime`, '%d/%m/%Y')
FROM myTable
WHERE dateTime BETWEEN '2011-09-28 18:00:00' AND '2011-10-29 18:59:00'
GROUP BY WEEK(dateTime)
The results given by MySQL take the first value of column dateTime, in this case 28/09/2011 which happens to be a Saturday.
Is it possible to adjust the query in MySQL to show the date upon which the week commences, even if there is no data available, so that for the above, 2011-09-28 would be replaced with 2011/09/26 instead? That is, the date of the start of the week, being a Monday. Or would it be better to adjust the dates programmatically after the query has run?
The dateTime column is in format 2011/10/02 12:05:00
It is possible to do it in SQL but it would be better to do it in your program code as it would be more efficient and easier. Also, while MySQL accepts your query, it doesn't quite make sense - you have DATE_FORMAT(dateTime, '%d/%m/%Y') in select's field list while you group by WEEK(dateTime). This means that the DB engine has to select random date from current group (week) for each row. Ie consider you have records for 27.09.2011, 28.09.2011 and 29.09.2011 - they all fall onto same week, so in the final resultset only one row is generated for those three records. Now which date out of those three should be picked for the DATE_FORMAT() call? Answer would be somewhat simpler if there is ORDER BY in the query but it still doesn't quite make sense to use fields/expressions in the field list which aren't in GROUP BY or which aren't aggregates. You should really return the week number in the select list (instead of DATE_FORMAT call) and then in your code calculate the start and end dates from it.
I want to order by date.
e.g.
table_date
February 2011
January 2011
December 2010
I've already tried:
SELECT distinct(table_date) FROM tables ORDER BY table_date DESC
bur it doesn't work.
I get this instead:
January 2011
February 2011
December 2010
Can you help me please?
If you must store the dates in a varchar which as others pointed out is not recommended, you could use:
SELECT table_date FROM tables ORDER BY STR_TO_DATE(table_date, '%M %Y') DESC;
If you want to order by date, store it as a date, not a string. Unless your date string is of the form yyyy-mm-dd, it will not sort as you want it.
Databases are hard enough work as-is, without people making it harder, and you should be striving as much as possible to avoid what I like to call SQL gymnastics.
Store it as a date then, if you must, use date functions to get it in the form February 2011.
It'll be a lot easier going that way than what you're trying to do.
Even if you can't change any of the current columns due to code restrictions, you can always add another column to the database like TABLE_DATE_AS_DATE and put in an insert/update trigger to populate it based on TABLE-DATE.
Then just do:
update table x set table_date = table_date
or something similar, to fire the trigger for all rows.
Then, your query can still get at table_date but use table_date_as_date for ordering. That's a kludge of course but I've had to use tricks like that in the past when it was imperative the code could not change, so we had to resort to DBMS trickery.
Store dates as DATE, not as VARCHAR, that's a huge mistake. Use STR_TO_DATE() to convert your content. When you're done, you can order by dates without any problems.
Date should be stored as date and not VARCHAR.
Suppose you have table_date in the following format (DD-MM-YYYY)
table_date
2011-01-01
2011-02-01
2010-12-01
Now you can perform order by clause in the following way
SELECT * FROM table_order ORDER BY str_to_date(date, "%Y-%M-%D") ASC
I doubt if the output will be in ordered form