I want all sum of counts between these 2 month-year here month and year are 2 separate fields.
How can I do that ? Thanks for help.
SELECT *
FROM foo
JOIN
SELECT (SUM(`full_lead_count`)) AS `domainFullLeadCount`
FROM `abc`.`reports`
WHERE ((`month` >= 10)
AND (`year` >= 2013))
AND ((`month` <= 3)
AND (`year` <= 2014)) LIMIT 1 bar ON val1 = val2
WHERE id = 123;
These premises are definitely not ideal. If it is possible to change the database layout, then you should consider to use real date datatypes from MySQL. Otherwise you could use STR_TO_DATE in order to convert your two fields into a single DATETIME, which can then be handled more easily.
Think of this as a comment rather than a useful answer...
Untested and very incomplete:
The issue is that if there was a range of year such as 2012 to 2014 then not all rows would be selected with the given example.
Something like the following will correctly select all the rows in the date range required:
SELECT (SUM(`full_lead_count`)) AS `domainFullLeadCount`
FROM `abc`.`reports`
WHERE ((`year` * 100 + `month`) >= 201310)
and ((`year` * 100 + `month`) <= 201403)
And, yes it isn't nice as indexes cannot be used etc.
Related
I have a field that is a a DATETIME field.
How do I select rows that fall between between 12:00 am and 8:30 am?
when I type in this query:
$query = "select * from table where TIME(changetime) between '12:00:00 and '08:30:00'";
it gives me zero hits.
The changetime field has the date like this: 2015-07-14 10:57:57
12:00:00 is 12 PM (noon), not 12 AM (midnight). You need to use
WHERE TIME(changetime) BETWEEN '00:00:00' AND '08:30:00'
1) you don't need TIME(changetime), it could probably worsen your search-time because mysql will need to apply a function instead of using index.
use just changetime.
2) where changetime between '2015-07-14 10:57:57' and '2015-07-14 12:57:57'
http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_between
3) you need to use right range - in your example END_DATE is BEFORE START_DATE. So, between will return 0 (12:00:00 > 08:30:00)
4) look at #Barmar's comment, maybe you really want entries between some time in any date, not between the datetimes
I have a fun one for you. I have a database with the date columns free_from and free_until. What I need to find is the amount of days between now and 1 month today which are free. For example, if the current date was 2013/01/15 and the columns were as follows:
free_from | free_until
2013/01/12| 2013/01/17
2013/01/22| 2013/01/26
2013/01/29| 2013/02/04
2013/02/09| 2013/02/11
2013/02/14| 2013/02/17
2013/02/19| 2013/02/30
The answer would be 16
as 2 + 4 + 6 + 2 + 2 + 0 = 16
The first row only starts counting at the 15th rather than the 12th
since the 15th is the current date.
The last row is discounted because none of the dates are within a
month of the current date.
The dates must be counted as it the free_from date is inclusive and
the free_until date is exclusive.
I'm assuming DATEDIFF() will be used somewhere along the line, but I can't, for the life of me, work this one out.
Thanks for your time!
Edit: This is going into PHP mysql_query so that might restrict you a little concerning what you can do with MYSQL.
SET #today = "2013-01-15";
SET #nextm = DATE_ADD(#today, INTERVAL 1 month);
SET #lastd = DATE_ADD(#nextm, INTERVAL 1 day);
SELECT
DATEDIFF(
IF(#lastd> free_until, free_until, #lastd),
IF(#today > free_from, #today, free_from)
)
FROM `test`
WHERE free_until >= #today AND free_from < #nextm
That should work. At least for your test data. But what day is 2013/02/30? :-)
Dont forget to change #today = CURDATE();
The best I can think of is something like:
WHERE free_until > CURDATE()
AND free_from < CURDATE() + INTERVAL '1' MONTH
That will get rid of any unnecessary rows. Then on the first row do in PHP:
date_diff(date(), free_until)
On the last row, do:
date_diff(free_from, strtotime(date("Y-m-d", strtotime($todayDate)) . "+1 month"))
Then on intermediate dates do:
date_diff(free_from, free_until)
Something to that effect, but this seems extremely clunky and convoluted...
From the top of my mind... first do a:
SELECT a.free_from AS a_from, a.free_until AS a_until, b.free_from AS b_from
FROM availability a
INNER JOIN availability b ON b.free_from > a.free_until
ORDER BY a_from, b_from
This probably will return a set of rows where for each row interval you have next i.e. greater intervals. The results are ordered strategically. You can then wrap the results in a partial group by:
SELECT * FROM (
SELECT a.free_from AS a_from, a.free_until AS a_until, b.free_from AS b_from
FROM availability a
INNER JOIN availability b ON b.free_from > a.free_until
ORDER BY a_from, b_from
) AS NextInterval
GROUP BY a_from, b_until
In the above query, add a DATE_DIFF clause (wrap it in SUM() if necessary):
DATE_DIFF(b_until, a_from)
I will like to understand the most efficient way to achieve this:
I have a MySQL table with my users info, including birthday (YYYY/MM/DD). My objective is to retrieve an array (php) with the total of user for each individual age from 10 to 60 years old. I can just query all my users, pass the birthday to a birthday to age php function I wrote and then populate an array with the totals. But I wonder if there's a way to build an sql query that does this job for me?
You should be able to group the rows:
SELECT
FLOOR(DATEDIFF(NOW(), `birthday`)/365) AS 'Age',
COUNT(*)
FROM `users`
WHERE FLOOR(DATEDIFF(NOW(), `birthday`)/365) BETWEEN 10 AND 60
AND `id` IN (1, 3, 5, 12, 29)
GROUP BY FLOOR(DATEDIFF(NOW(), `birthday`)/365)
ORDER BY FLOOR(DATEDIFF(NOW(), `birthday`)/365) ASC
The result won't contain ages that have no users, but I'm not sure why you'd need that.
Updates
- Added an id filter
- Fixed the date calculation (oops!)
- Named the new column
SELECT * FROM tableName
WHERE dateOfBirth >= (CURRENT_DATE - INTERVAL 60 YEAR)
AND dateOfBirth <= ( CURRENT_DATE - INTERVAL 10 YEAR )
Hopefully I don't get slammed by the database experts on this one...
You will need to use MySql's DATEDIFF().
SELECT USER, DATEOFBIRTH FROM USERTABLE WHERE DATEDIFF(DATEOFBIRTH,NOW()) < 60;
I want to be able to fetch results from mysql with a statement like this:
SELECT *
FROM table
WHERE amount > 1000
But I want to fetch the result constrained to a certain a month and year (based on input from user)... I was trying like this:
SELECT *
FROM table
WHERE amount > 1000
AND dateStart = MONTH('$m')
...$m being a month but it gave error.
In that table, it actually have two dates: startDate and endDate but I am focusing on startDate. The input values would be month and year. How do I phrase the SQL statement that gets the results based on that month of that year?
You were close - got the comparison backwards (assuming startDate is a DATETIME or TIMESTAMP data type):
SELECT *
FROM table
WHERE amount > 1000
AND MONTH(dateStart) = {$m}
Caveats:
Mind that you are using mysql_escape_string or you risk SQL injection attacks.
Function calls on columns means that an index, if one exists, can not be used
Alternatives:
Because using functions on columns can't use indexes, a better approach would be to use BETWEEN and the STR_TO_DATE functions:
WHERE startdate BETWEEN STR_TO_DATE([start_date], [format])
AND STR_TO_DATE([end_date], [format])
See the documentation for formatting syntax.
Reference:
MONTH
YEAR
BETWEEN
STR_TO_DATE
Use the month() function.
select month(now());
Try this:
SELECT *
FROM table
WHERE amount > 1000 AND MONTH(dateStart) = MONTH('$m') AND YEAR(dateStart) = YEAR('$m')
E.g.
$date = sprintf("'%04d-%02d-01'", $year, $month);
$query = "
SELECT
x,y,dateStart
FROM
tablename
WHERE
AND amount > 1000
AND dateStart >= $date
AND dateStart < $date+Interval 1 month
";
mysql_query($query, ...
This will create a query like e.g.
WHERE
AND amount > 1000
AND dateStart >= '2010-01-01'
AND dateStart < '2010-01-01'+Interval 1 month
+ Interval 1 month is an alternative to date_add().
SELECT Date('2010-01-01'+Interval 1 month)-> 2010-02-01
SELECT Date('2010-12-01'+Interval 1 month)-> 2011-01-01
This way you always get the first day of the following month. The records you want must have a dateStart before that date but after/equal to the first day of the month (and year) you've passed to sprintf().
'2010-01-01'+Interval 1 month doesn't change between rows. MySQL will calculate the term only once and can utilize indices for the search.
Try this
SELECT *
FROM table
WHERE amount > 1000
AND MONTH(datestart)
GROUP BY EXTRACT(YEAR_MONTH FROM datestart)
Try this if(date field is text then convert this string to date):
SELECT * FROM `table_name` WHERE MONTH(STR_TO_DATE(date,'%d/%m/%Y'))='11'
//This will give month number MONTH(STR_TO_DATE(date,'%d/%m/%Y'))
//If its return 11 then its November
// Change date format with your date string format %d/%m/%Y
Works in: MySQL 5.7, MySQL 5.6, MySQL 5.5, MySQL 5.1, MySQL 5.0, MySQL 4.1, MySQL 4.0, MySQL 3.23
Day:
SELECT EXTRACT(DAY FROM "2017-06-15");
Month:
SELECT EXTRACT(MONTH FROM "2017-06-15");
Year:
SELECT EXTRACT(YEAR FROM "2017-06-15");
I'm looking for a best practice advice how to speed up queries and at the same time to minimize the overhead needed to invoke date/mktime functions. To trivialize the problem I'm dealing with the following table layout:
CREATE TABLE my_table(
id INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT,
important_data INTEGER,
date INTEGER);
The user can choose to show 1) all entries between two dates:
SELECT * FROM my_table
WHERE date >= ? AND date <= ?
ORDER BY date DESC;
Output:
10-21-2009 12:12:12, 10002
10-21-2009 14:12:12, 15002
10-22-2009 14:05:01, 20030
10-23-2009 15:23:35, 300
....
I don't think there is much to improve in this case.
2) Summarize/group the output by day, week, month, year:
SELECT COUNT(*) AS count, SUM(important_data) AS important_data
FROM my_table
WHERE date >= ? AND date <= ?
ORDER BY date DESC;
Example output by month:
10-2009, 100002
11-2009, 200030
12-2009, 3000
01-2010, 0 /* <- very important to show empty dates, with no entries in the table! */
....
To accomplish option 2) I'm currently running a very costly for-loop with mktime/date like the following:
for(...){ /* example for group by day */
$span_from = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i, date("Y", $time_min));
$span_to = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i+1, date("Y", $time_min));
$query = "..";
$output = date("m-d-y", ..);
}
What are my ideas so far? Add additional/ redundant columns (INTEGER) for day (20091212), month (200912), week (200942) and year (2009). This way I can get rid of all the unnecessary queries in the for loop. However I'm still facing the problem to very fastly calculate all dates that doesn't have any equivalent in database. One way to simply move the problem could be to let MySQL do the job and simply use one big query (calculate all the dates/use MySQL date functions) with a left join (the data). Would it be wise to let MySQL take the extra load? Anyway I'm reluctant to use all these mktime/date in the for loop. Since I have complete control over the table layout and code even suggestions with major changes are welcome!
Update
Thanks to Greg I came up with the following SQL query. However it still bugs me to use 50 lines of sql statements - build up with php - that maybe could be done faster and more elegantly otherwise:
SELECT * FROM (
SELECT DATE_ADD('2009-01-30', INTERVAL 0 DAY) AS day UNION ALL
SELECT DATE_ADD('2009-01-30', INTERVAL 1 DAY) AS day UNION ALL
SELECT DATE_ADD('2009-01-30', INTERVAL 2 DAY) AS day UNION ALL
SELECT DATE_ADD('2009-01-30', INTERVAL 3 DAY) AS day UNION ALL
......
SELECT DATE_ADD('2009-01-30', INTERVAL 50 DAY) AS day ) AS dates
LEFT JOIN (
SELECT DATE_FORMAT(date, '%Y-%m-%d') AS date, SUM(data) AS data
FROM test
GROUP BY date
) AS results
ON DATE_FORMAT(dates.day, '%Y-%m-%d') = results.date;
You definitely shouldn't be doing a query inside a loop.
You can group like this:
SELECT COUNT(*) AS count, SUM(important_data) AS important_data, DATE_FORMAT('%Y-%m', date) AS month
FROM my_table
WHERE date BETWEEN ? AND ? -- This should be the min and max of the whole range
GROUP BY DATE_FORMAT('%Y-%m', date)
ORDER BY date DESC;
Then pull these into an array keyed by date and loop over your data range as you are doing (that loop should be pretty light on CPU).
Another idea is not to use string inside the query. Transform the string parameter to datetime, on mysql.
STR_TO_DATE(str,format)
http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html