I have a PHP application that records the sessions of various devices connected to a server. The database has a table session, with the columns device_id, start_date, end_date. To know the number of devices connected on a given date, I can use the request :
SELECT COUNT(DISTINCT device_id)
FROM session
WHERE :date >= start_date AND (:date <= end_date OR end_date IS NULL)
where :date is passed as a parameter to the prepared statement.
This works fine, but if I want to know the number of devices for every days of the year, that makes 365 queries to run, and I'm afraid things could get very slow. It doesn't feel right to be iterating on the date in PHP, it seems to me that there should be a more optimal way to do this, with a single query to the database.
Is it possible do this with a single query?
Would it actually be faster than to iterate on the date in PHP an running multiple queries?
EDIT to answer the comments :
I do want the number for each separate day (to draw a graph for example), not just the sum
the datatype is DATE
If I understand correctly then you first need a table of dates, something like:
create table dates(dt date);
insert into dates(dt) values
('2001-01-01'),
('2001-01-02'),
...
('2100-12-31')
And use a query like so:
select dates.dt, count(session.device_id)
from dates
join session on start_date <= dates.dt and (dates.dt <= end_date or end_date is null)
-- change to left join to include zero counts
where dates.dt >= :date1 and dates.dt <= :date2
group by dates.dt
PS: since you mentioned charts I might add that it is possible to avoid the table of dates. However, the result will only contain dates on which the count of devices changed. Chart APIs usually accept this kind of data but still create data points for all dates in between.
Related
I am working on a school project where I have to make a todo web app. now i have a little problem. I need to get the records that are running out of time (think 20% of the whole task left). now i'm looking for a solution in php or a sql statement with which i can retrieve only those records.
I tried many statements but i cant get one to work.
SELECT * FROM tasks
WHERE user_id='$user_id'
AND '$currentDate' BETWEEN start_date AND end_date
The above one is working with the date but not with time.
So now I need to have a statement or function that only retrieves the tasks that are almost finished. I've added a screenshot of the database and the application to clarify it a bit.
i hope someone could help me. (this is my first time using stackoverflow so sorry if i do something wrong)
First, you should not be munging your query with constants, date or otherwise. So, use now().
Second, combine the date/time into a single column
Third, you seem to want and:
WHERE user_id = ? AND
NOW() >= start_datetime AND
NOW() < end_datetime
If you want to store the date/time in separate columns, then you can combine them:
WHERE user_id = ? AND
NOW() >= ADDTIME(CAST(start_date as DATETIME), start_time) AND
NOW() < ADDTIME(CAST(end_date as DATETIME), end_time)
I have a PHP scirpt that is always querying all the data from a database table and it's getting pretty slow. I really just need the data of a specific month and year.
Is there a simple way to get only those entries? For example, everything from February 2013?
The column that stores the dates in my table is of type datetime, if that applies to the solution.
You can add that condition in the WHERE clause of your select statement. I would recommend using BETWEEN operand for two dates:
SELECT myColumns
FROM myTable
WHERE dateColumn BETWEEN '2013-02-01' AND '2013-02-28';
If you mean to say you want everything beginning with February 2013, you can do so using the greater than or equal to operator:
SELECT myColumns
FROM myTable
WHERE dateColumn >= '2013-02-01';
EDIT
While the above are my preferred methods, I would like to add for completeness that MySQL also offers functions for grabbing specific parts of a date. If you wanted to create a paramaterized query where you could pass in the month and year as integers (instead of a start and end date) you could adjust your query like this:
SELECT myColumns
FROM myTable
WHERE MONTH(dateColumn) = 2 AND YEAR(dateColumn) = 2013;
Here is a whole bunch of helpful date and time functions.
You should index the datetime field for added efficiency and then use Between syntax in your sql. This will allow the mysql engine to remove all records that you are not interested in from the returned data set.
my question is more "theoretical" than practical - in other words, Im not really looking for a particular code for how to do something, but more like an advice about how to do it. Ive been thinking about it for some time but cannot come up with some feasible solution.
So basically, I have a MySQL database that saves weather information from my weather station.
Column one contains date and time of measurement (Datetime format field), then there is a whole range of various columns like temp, humidity etc. The one I am interested in now is the one with the temperature. The data is sorted by date and time ascending, meaning the most recent value is always inserted to the end.
Now, what I want to do is using a PHP script, connect to the db and find temperature changes within a certain interval and then find the maximum. In other words, for example lets say I choose interval 3h. Then I would like to find the time, from all the values, where there was the most significant temperature change in those 3 h (or 5h, 1 day etc.).
The problem is that I dont really know how to do this. If I just get the values from the db, Im getting the values one by one, but I cant think of a way of getting a value that is lets say 3h from the current in the past. Then it would be easy, just subtracting them and get the date from the datetime field at that time, but how to get the values that are for example those 3 h apart (also, the problem is that it cannot just simply be a particular number of rows to the past as the intervals of data save are not regular and range between 5-10mins, so 3 h in the past could be various number of rows).
Any ideas how this could be done?
Thx alot
Not terribly hard actually. So I would assume it's a two column table with time and temp fields, where time is a DATETIME field
SELECT MAX(temp) FROM records
WHERE time >= "2013-10-14 12:00:00" and time <= "2013-10-14 15:00:00"
SELECT t1.*, ABS(t1.temperature - t2.temperature) as change
FROM tablename t1
JOIN tablename t2
ON t2.timecolumn <= (t1.timecolumn - INTERVAL 3 HOUR)
LEFT JOIN tablename t3
ON t3.timecolumn <= (t1.timecolumn - INTERVAL 3 HOUR)
AND t2.timecolumn > t3.timecolumn
WHERE
t3.some_non_nullable_column IS NULL
ORDER BY ABS(t1.temperature - t2.temperature) DESC
LIMIT 1;
1 table joined 2 times on itself, t2 is the quaranteed direct predecessor of t1 t2 is the closest record with offset 3h before or more. This could with the proper indexes, and a limited amount of data (where limited is in the eye of the beholder) be quite performant. However, if you need a lot of those queries in a big dataset, this is a prime candidate for denormalization, were you create a table which also stores the calculated offsets compared to the previous entry.
I have a table called schedules which contains columns day, month, year, etc. What I need is to select records between the $datefrom and $dateto. Here is my code that does not work :(
SELECT * FROM schedules WHERE CONCAT(year, month, day) BETWEEN $datefrom AND $dateto
Im not sure if this is correct. Please help.
Like showdev already said in a comment, you have to cast the string that is returned from CONCAT() function to date. But consider, that no index can be used on this.
I'd suggest you create an additional column in your table with the full date. I don't know if you separated the date into 3 columns out of performance reasons, but have a try, if only one column is enough for you. Usually it's fast enough (when indexed).
If you don't want to do that and want to use indexes (if they exist at all on those 3 columns) you would have to write the query like this:
SELECT * FROM schedules WHERE
`year` BETWEEN YEAR($datefrom) AND YEAR($dateto)
AND `month` BETWEEN MONTH($datefrom) AND MONTH($dateto)
AND `day` BETWEEN DAY($datefrom) AND DAY($dateto)
Let's assume I manage medical patient stays information system.
I want to get the patient count per day with the following minimal structure :
stay table has begin and end datetime columns
PHP gives me $first_day and $last_day limits
The following snippet is NOT what I want, since it only counts entries per day, and not present stays per day:
SELECT
DATE_FORMAT(`stay`.`begin`, '%Y-%m-%d') AS `date`,
COUNT(`stay`.`stay_id`) AS `total`
FROM `stay`
WHERE `stay`.`begin` <= '$first_day'
AND `stay`.`end` >= '$last_day'
GROUP BY `date`
ORDER BY `date`
Last but not least, I'm looking for a full SQL query.
It goes without saying that making one SQL query for each day would be totally trivial.
Use of temporary (dates ?) table is clearly an option.
As you mentioned using a temporary table of all dates in the range you want is one way to handle this. If you created a table of date called foo with all dates between $first_day and $last_day inclusive (see here).
Then you can write your query like:
SELECT f.date, count(s.stay_id)
FROM foo f
JOIN stay s ON s.begin <= f.date AND s.end >= date
GROUP BY f.date
ORDER BY f.date
A quick Google around leads me to this page: What is the most straightforward way to pad empty dates in sql results (on either mysql or perl end)?
What I would suggest is that you either follow the advice in that question, or construct your own loop in PHP.