SQL - add days to a date in a loop - mysql

I have a table with this fields:
Id int(11) pk,
Start_date date,
End_date date,
type tinyint(1),
Frequency int.
I want to do a select on this table where start_date+frequency = #date(a variable date) until end_date(loop).
How do this with sql?
Thanks in advance.
EDIT:
Variable date is (for example):
SET #date = '2017-03-30'
type can be 0 or 1:
if type = 0 my query is :
select * from table
where type = 0 and start_date <= #date AND end_date>=#date
if type = 1, frequency is a field with an integer number(a interval of days). So I have to check if adding this value to start_date is equals to #date.
if yes, I have to return the current record
if no, I have to iterate this operation
Date current = start_date + interval of 'frequency' days
while(current < end_date){
if(current == #date)
(this is the record I want)
else
current+=frequency
}
The result of query of type 1 can be more than one record. And finally I want to UNION the result of type 0 and 1 in unique select.

Based on a comment/confirmation below the question:
So, to put it in less focussed on the wrong solution terms, you want to determine whether the difference between start_date and #date, in days, is an integer multiple of frequency? –
Looks like you want something like:
select * from table
where
start_date <= #date AND
end_date>=#date AND
(
type = 0 OR
(
type = 1 AND
mod(datediff(#date,start_date),frequency) = 0
)
)
Once we determined the actual requirement, above, and it was clear we just need to find out if one number is a multiple of another, we use mod to compute that. The rest of the structure of the WHERE clause essentially follows the bullet-pointed section of the question.

Related

MySQL how to query data with time now to start date and end date

i want to get data from table, and example of data like this:
Event Name
Start Date
End Date
Event 1
2022-07-30 00:00:00
2022-08-06 23:59:59
Event 2
2022-08-08 00:00:00
2022-08-15 23:59:59
value of example is "2022-08-07 00:00:00", what i want is get data "Event 1" as the latest event because there is no event starting in "2022-08-07".
and when the value of example "2022-08-08 12:12:12", what i want is get data "Event 2" because there is a event starting from that date. And when the value is "2022-08-09 08:00:00" i want to still get the data "Event 2", because the date is still lower than end date.
How to query in MySQL, so i can result like that?
Assuming proper column names, it would be as below for your initial query (swap out the date for each of your examples):
SELECT *
FROM table
WHERE StartDate <= "2022-08-07 00:00:00"
ORDER BY StartDate desc
LIMIT 1;
Of course if you're basing it of when you run the SQL:
SELECT *
FROM table
WHERE StartDate <= now()
ORDER BY StartDate desc
LIMIT 1;
My approach would be the following nested SELECTs - replace the mentioned timestamp '2022-08-16 12:12:12' with NOW() or any timestamp you need in your case
SELECT * FROM
(
(
SELECT a.*,-1 AS mynumber
FROM `testtest2` AS a
WHERE start<='2022-08-16 12:12:12' AND end >='2022-08-16 12:12:12'
)
UNION
(
SELECT a.*,b.mynumber
FROM `testtest2` AS a,
(SELECT COUNT(*) AS mynumber FROM `testtest2` WHERE start<='2022-08-16 12:12:12' AND end >='2022-08-16 12:12:12') AS b
WHERE end<='2022-08-16 12:12:12' ORDER BY end DESC LIMIT 1
)
) c
WHERE mynumber<=0
Explanation
One SELECT retrieves all events currently active (start <= timestamp AND end >= timestamp) and sets the column mynumber to the fixed value -1 (which clearly is < 0).
The next SELECT retrieves events that have already passed, sorted descending by the column end and only retrieve the first (= newest/last) event. The column mynumber is filled with the numer of events that are currently running - so this column contains either a 0 (if there are no events currently running) or a positive number (> 0) if there are other events running.
These two SELECTs are combined using UNION and used as source for the outer SELECT which only retrieves events with a value <=0 for the column mynumber.
This way you get either the active events OR (in case there are no active events) the last/newest event which has already passed.

Rows with a start_date and end_date, need to return records that fall within a date range

I have a table that contains tasks, each task has a date_start and date_finish field.
I need to construct a query which will take a passed in date and return all rows if that passed in date falls between the date_start and date_finish.
Does this make sense?
I have been trying to use standard date type querys such as:
SELECT *
FROM project_task
WHERE project_task.date_start >= '2013-10-10' AND project_task.date_finish <= '2013-10-10'
but it doesn’t return the correct results and using BETWEEN does not work either because I need it to take into account both fields (date_start and date_finish) not just the one.
I think it may only be the WHERE part of the query I need.
You've made a simple mistake, you got the order of your operators wrong:
SELECT *
FROM project_task
WHERE
project_task.date_start <= '2013-10-10' -- start should before the test date
AND
project_task.date_finish >= '2013-10-10' -- and finish after the test date
Explanation
If the date checked is between date_start and date_finish, then we must have reached at last the start date. We could have a later date too. That means the
date_start will be lower or equal than the date checked
And the second check said: the finish date musn't have passed. So
date_finish has to be greater or equal than the date checked.
With your original query you will only get projects that start and end on '2013-10-10'.
Demo
DECLARE #Now datetime = '2013-10-10T00:00:00'
SELECT *
FROM project_task a
WHERE #Now >= a.Date_Start
AND #Now < isnull(a.Date_Finish, '9999-12-31T00:00:00')

How to select based on different column data

I want to perform a different SELECT based on the column data. For example I have a table http://sqlfiddle.com/#!2/093a2 where I want compare start_date and end_date only if use_schedule = 1. Otherwise select all data. (A different select) Basically I only want to compare the start and end date if only use_schedule is 1 and if use_schedule is 0 then select rest of the data.
An example may be something like
select id, name from table
where use_schedule = 0
else
select id, name, start_date from table
where use_schedule = 0 and current_date >= start_date.
Basically I have the data where schedule is enabled only then look into start and end date. Because if schedule is not enabled there is no point of looking into the dates. Just select the data. With schedule enabled, I want to be more selective in selecting the scheduled data.
I am trying to figure out if MySQL CASE or IF statements would work but not able to do so. How can I run this select?
Thanks.
You can use UNION to mix and match the results of 2 different SQL queries into one result set:
select id, name, null from table
where use_schedule = 0
union
select id, name, start_date from table
where use_schedule = 1 and current_date >= start_date
Note that both queries have to have compatible output fields (same number and type for this to work). The use of UNION automatically merges only distinct records - if you want to keep double results use UNION ALL instead.
In this specific case a more extensive WHERE-clause would also work obviously:
where use_schedule = 0 or (use_schedule = 1 and current_date >= start_date)
But given the question I'm assuming your real case is a bit more complex.
Documentation over at MySQL site.
Use CASE, in this case..:
SELECT id, name,
(CASE
WHEN start_date >= DATE(NOW()) AND use_schedule = 1
THEN start_date
ELSE NULL
END) AS cols FROM campaigns
This way it selects only the schedule 0 OR the 1 with a date bigger or equals to now;
I used DATE(NOW()) so that it removes the time which you are not interested in.

TSQL - Calculating datediff in a query and setting a value?

Using SQL Server 2008, I want to calculate the timespan, in seconds, that has occurred between two times.
The start date, is the timestamp of the last occurance of where a specific ID exists (if the filter is true), get the time from that timestamp record, and do a DATEDIFF() against the current processing time and return a value, #LastEventTimespan, in seconds.
DECLARE #CurrentProcessTime DATETIME
DECLARE #LastEventTimespan DATETIME
SET #CurrentProcessTime = GetDate()
-- find the timespan since the last session event
-- DATEDIFF ( datepart , startdate , enddate )
SELECT MAX(PageVisitEventID) AS LastPageVisitEventID, #LastEventTimespan = DATEDIFF(second , DateAdded , #CurrentProcessTime )
FROM PageVisitEvents
WHERE UserID = #UserID
GROUP BY LastPageVisitEventID
I figured I could get the MAX ID of the filter and process accordingly but am unable to set the #LastEventTimespan, however trying to assign a value when doing data-retrieval is a no-no.
How can I get around this?
Thanks.
I guess you want something like this.
DECLARE #LastEventTimespan INT
SELECT TOP 1 #LastEventTimespan = DATEDIFF(SECOND, DateAdded, GETDATE())
FROM PageVisitEvents
WHERE UserID = #UserID
ORDER BY PageVisitEventID DESC
This will calculate the difference in seconds between DateAdded for the highest value of PageVisitEventID for a given user and the current DateTime. I changed the data type of #LastEventTimespan to INT because it probably makes more sense when dealing with seconds.
You can replace the SELECT statement with this one:
SELECT TOP 1
#LastEventTimespan = DATEDIFF(second , DateAdded , #CurrentProcessTime )
FROM PageVisitEvents
WHERE UserID = #UserID
ORDER BY PageVisitEventID desc
I've done such queries many times and never had problems.

Group by day and still show days without rows?

I have a log table with a date field called logTime. I need to show the number of rows within a date range and the number of records per day. The issue is that i still want to show days that do not have records.
Is it possible to do this only with SQL?
Example:
SELECT logTime, COUNT(*) FROM logs WHERE logTime >= '2011-02-01' AND logTime <= '2011-02-04' GROUP BY DATE(logTime);
It returns something like this:
+---------------------+----------+
| logTime | COUNT(*) |
+---------------------+----------+
| 2011-02-01 | 2 |
| 2011-02-02 | 1 |
| 2011-02-04 | 5 |
+---------------------+----------+
3 rows in set (0,00 sec)
I would like to show the day 2011-02-03 too.
MySQL will not invent rows for you, so if the data is not there, they will naturally not be shown.
You can create a calendar table, and join in that,
create table calendar (
day date primary key,
);
Fill this table with dates (easy with a stored procedure, or just some general scripting), up till around 2038 and something else will likely break unitl that becomes a problem.
Your query then becomes e.g.
SELECT logTime, COUNT(*)
FROM calendar cal left join logs l on cal.day = l.logTime
WHERE day >= '2011-02-01' AND day <= '2011-02-04' GROUP BY day;
Now, you could extend the calendar table with other columns that tells you the month,year, week etc. so you can easily produce statistics for other time units. (and purists might argue the calendar table would have an id integer primary key that the logs table references instead of a date)
In order to accomplish this, you need to have a table (or derived table) which contains the dates that you can then join from, using a LEFT JOIN.
SQL operates on the concept of mathematical sets, and if you don't have a set of data, there is nothing to SELECT.
If you want more details, please comment accordingly.
I'm not sure if this is a problem that should be solved by SQL. As others have shown, this requires maintaining a second table that contains the all of the individual dates of a given time span, which must be updated every time that time span grows (which presumably is "always" if that time span is the current time.
Instead, you should use to inspect the results of the query and inject dates as necessary. It's completely dynamic and requires no intermediate table. Since you specified no language, here's pseudo code:
EXECUTE QUERY `SELECT logTime, COUNT(*) FROM logs WHERE logTime >= '2011-02-01' AND logTime <= '2011-02-04' GROUP BY DATE(logTime);`
FOREACH row IN query result
WHILE (date in next row) - (date in this row) > 1 day THEN
CREATE new row with date = `date in this row + 1 day`, count = `0`
INSERT new row IN query result AFTER this row
ADVANCE LOOP INDEX TO new row (`this row` is now the `new row`)
END WHILE
END FOREACH
Or something like that
DECLARE #TOTALCount INT
DECLARE #FromDate DateTime = GetDate() - 5
DECLARE #ToDate DateTime = GetDate()
SET #FromDate = DATEADD(DAY,-1,#FromDate)
Select #TOTALCount= DATEDIFF(DD,#FromDate,#ToDate);
WITH d AS
(
SELECT top (#TOTALCount) AllDays = DATEADD(DAY, ROW_NUMBER()
OVER (ORDER BY object_id), REPLACE(#FromDate,'-',''))
FROM sys.all_objects
)
SELECT AllDays From d