I have a scenario where I need to pull up delivery dates based on a table below (Example)
job_id | delivery_date
1 | 2013-01-12
2 | 2013-01-25
3 | 2013-02-15
What I'm trying to do is show the user all the delivery dates that start with the earliest (in this case it would be 2013-01-12) and add an another 21 days to that. Basically, the output I would expect it to show of course, the earliest date being the starting date 2013-01-12 and 2013-01-25. The dates past the February date are of no importance since they're not in my 21 date range. If it were a 5 day range, for example, then of course 2013-01-25 would not be included and only the earliest date would appear.
Here is main SQL clause I have which only shows jobs starting this year forward:
SELECT date, delivery_date
FROM `job_sheet`
WHERE print_status IS NULL
AND job_sheet.date>'2013-01-01'
Is it possible to accomplish this with 1 SQL query, or must I go with a mix of PHP as well?
You can use the following:
select *
from job_sheet
where print_status IS NULL
and delivery_date >= (select min(delivery_date)
from job_sheet)
and delivery_date <= (select date_add(min(delivery_date), interval 21 day)
from job_sheet)
See SQL Fiddle with Demo
If you are worried about the dates not being correct, if you use a query then it might be best to pass in the start date to your query, then add 21 days to get the end date. Similar to this:
set #a='2013-01-01';
select *
from job_sheet
where delivery_date >= #a
and delivery_date <= date_add(#a, interval 21 day)
See SQL Fiddle with Demo
SELECT date,
delivery_date
FROM job_sheet
WHERE print_status IS NULL
AND job_sheet.date BETWEEN (SELECT MIN(date) FROM job_sheet) AND
(SELECT MIN(date) FROM job_sheet) + INTERVAL 21 DAY
SELECT j.job_id
, j.delivery_date
FROM `job_sheet` j
JOIN ( SELECT MIN(d.delivery_date) AS earliest_date
FROM `job_sheet` d
WHERE d.delivery_date >= '2013-01-01'
) e
ON j.delivery_date >= e.earliest_date
AND j.delivery_date < DATE_ADD(e.earliest_date, INTERVAL 22 DAY)
AND j.print_status IS NULL
ORDER BY j.delivery_date
(The original query has a predicate on job_sheet.date; the query above references the d.delivery_date... change that if it is supposed to be referencing the date column instaed.)
If the intent is to only show delivery_date values from today forward, then change the literal '2013-01-01' to an expression that returns the current date, e.g. DATE(NOW())
Related
I have a question. So I have this data in array :
id idm amount date
1 5 10 2017-08-23 12:12:12
2 5 20 2017-08-23 12:14:16
3 6 13 2017-08-23 18:00:00
4 5 25 2017-08-24 19:00:00
5 5 160 2017-08-24 19:30:00
So the idea is to get the sum of amount from date 2017-08-23 and compare with date 2017-08-24. If difference between those 2 values for a user is bigger than 20 for example in this case I found a user.
My propose was to make 2 sql's :
select sum(amount) as previous_amount, idm
FROM table
WHERE date >= '2017-08-23 00:00:00' AND date <= '2017-08-23 23:59:59'
GROUP By idm
select sum(amount) as actual_amount, idm
FROM table
WHERE date >= '2017-08-24 00:00:00' AND date <= '2017-08-24 23:59:59'
GROUP By idm
And make the treatment in php, but maybe exist a methode to do that in sql. Can you help me please ? Thx in advance and sorry for my english.
If I correctly understand, this is what you want:
select
idm,
sum(case when date >= '2017-08-23 00:00:00' AND date <= '2017-08-23 23:59:59' then amount end) -
sum(case when date >= '2017-08-24 00:00:00' AND date <= '2017-08-24 23:59:59' then amount end) as diff
from your_table
group by idm
having diff not between -20 and 20
Not tested but this might work..
select (sum(b.amount) - sum(a.amount)) as result, idm
from table a join table b on a.idm = b.idm
where a.date = date('2017-08-23') and b.date = date('2017-08-24')
group by idm
if you can provide a fiddle that might help us help you.
My approach would be to calculate the differences per idm and day (which I 'll show you how) and do the rest in the code. So looking at this first part, I am using the datetime manipulation functions to remove the time element out of the datetime values which keeps date only as string. This is now something we can group on:
select sum(amount) as previous_amount, idm, DATE_FORMAT(`date`, '%Y-%m-%d') as day
FROM test
GROUP By idm, day
order by idm, day
This will give you something like:
previous_amount idm day
30 5 2017-08-23
185 5 2017-08-24
13 6 2017-08-23
At this point I would continue in code. However, if you want to do this in the database you need to join the above result table on itself. This simulates a LAG/LEAD behavior (two Oracle instructions that are not available in MySQL). So the query is:
select
diff1.idm,
previous_amount,
actual_amount,
diff2.actual_amount-diff1.previous_amount as difference,
diff1.day as from_day,
diff2.day as to_day
from (
select sum(amount) as previous_amount, idm, DATE_FORMAT(`date`, '%Y-%m-%d') as day
FROM test
GROUP By idm, day
order by idm, day
) as diff1
left join (
select sum(amount) as actual_amount, idm, DATE_FORMAT(`date`, '%Y-%m-%d') as day
FROM test
GROUP By idm, day
order by idm, day
) as diff2 on diff1.idm=diff2.idm
where DATE(diff2.day) > DATE(diff1.day)
and diff2.actual_amount-diff1.previous_amount > 20;
The result of the above looks like:
idm previous_amount actual_amount difference from_day to_day
5 30 185 155 2017-08-23 2017-08-24
Note that the above query guarantees that day2 > day1 but does not enforce one day only difference. However, I think it could get extended to do so fairly easily.
sqlfiddle here
UPDATE #1
If you want to guarantee single day difference then replace the where condition with TIMESTAMPDIFF(DAY, diff1.day, diff2.day) = 1 fiddle
Try that, should be faster than #Shuddh solution because it does not rely on a join
SELECT
idm,
sum(IF(date(`date`) = '2017-08-23', amount, 0)) as amountDay1,
sum(IF(date(`date`) = '2017-08-24', amount, 0)) as amountDay1,
sum(IF(date(`date`) = '2017-08-23', amount, 0))
- sum(IF(date(`date`) = '2017-08-24', amount, 0)) as diffDay1Day2
FROM
table
GROUP BY
idm
I have the following table :
administratieID | varchar
getalTypeNaam | varchar
datum | date
waarde | double
With the following query:
Select administratieID AS AI, waarde WA, datum DT
from getalType
where getalTypeNaam = 'test'
and datum between DATE_SUB(NOW(), INTERVAL 10 DAY) and DATE_SUB(NOW(), INTERVAL 15 DAY)
order by datum
What I want is that I can do a select where all the dates between the specified dates are returned even if there is nothing in the database that coresponds with that date. I want this even if there is no value but in that case i want to return the value 0.
So I want this in return:
date administratieID waarde
10-10-2016 xxx 10
11-10-2016 xxx 0 <-- no data available
12-10-2016 xxx 40
13-10-2016 xxx 9
14-10-2016 xxx 0 <-- no data available
Now I get this:
date administratieID waarde
10-10-2016 xxx 10
12-10-2016 xxx 40
13-10-2016 xxx 9
There are ways to do this, but they are quite hackish...
One would be to have an inline incrementing variable, and use a dummy table that you know has at least as many records in it as the maximum number of days in your date range. Then you can do something like this:
SET #date = DATE_SUB(NOW(), INTERVAL 15 DAY); #or whatever the earliest date you need is
SELECT DISTINCT
IFNULL(administratieID,'XXX') AS AI
, IFNULL(waarde,0) AS WA
, all_dates AS DT
FROM
(SELECT #date:=DATE_ADD(#date, INTERVAL 1 DAY) AS all_dates
FROM
my_dummy_table_with_lots_of_records
WHERE #date >= DATE_SUB(NOW(), INTERVAL 15 DAY) AND #date <= DATE_SUB(NOW(), INTERVAL 10 DAY)) d LEFT JOIN
getalType t ON
DATE_FORMAT(all_dates,'%Y-%m-%d') = DATE_FORMAT(t.datum,'%Y-%m-%d')
WHERE
IFNULL(getalTypeNaam, 'Test') = 'Test' #if there's no data we still need to return something
ORDER BY all_dates;
Another easier way is just to have a table called "all_dates", which you can use in the join. The table would just have one column, "datum" and you populate it with every possible date that could be used in the getalType table. This could even be done with a trigger.
Then you link to this table, instead of doing all the derived nonsense above.
This would also avoid having to have multiple sql statements, as you don't have a variable that needs initialising.
I have a question with mysql.
I have a table like records and records have a field registered_date which contains the dates. I want to select last n months but not before the current date. I must select the last date that registered_date contains.
registered_date
2015-05-30
2015-05-29
2015-05-28
2015-05-27
...
and we are in july. The last date is 2015-05-30. I want to select last 3 months before the 2015-05-30.
I tried to like these:
.... where registered_date > DATE_SUB(now(), INTERVAL 6 MONTH)
and
registered_Date between now() - interval 30 day and now()
Thank you.
You need to find the biggest date and then select three months before that:
select t.*
from table t cross join
(select max(registered_date) as maxrd from table t) m
where t.registered_date >= maxrd - interval 3 month;
Did you try like this
SELECT * FROM table WHERE registered_date >= now()-interval 3 month;
I am having a trouble with OR condition inside the SELECT.
I want a simple result if one condition is matched and rest OR condition should not be use.
What i want is:
I have some users shared records and i would like to email them the newest items shared on my website.
For me: Newest Items will be least two days older
Like Today is 9th so i would like to pull all records of 7th. but if i
didn't get any record of 7th then i would like to pull all record of
6th (3 days older from today). if i didn't get any records on 6th then
i would like to pull 1 day older from today.
for all this i have used OR in my SELECT query like this:
SELECT `tg`.* FROM `tblgallery` AS `tg` WHERE (
(tg.added_date BETWEEN '2014-07-07 00:00:00' AND '2014-07-08 00:00:00') OR
(tg.added_date BETWEEN '2014-07-06 00:00:00' AND '2014-07-07 00:00:00') OR
(tg.added_date BETWEEN '2014-07-08 00:00:00' AND '2014-07-09 00:00:00') )
And i have records in my database for dates:
2014-07-06
2014-07-07
and when i run this query it gives me all record of both dates.
But I need to pull only record of 2014-07-07 not of both.(I have mentioned above.)
I know i can do this by using multiple Select and i think that will not be a good idea to request to database again and again.
My Question is : How to pull data from database if the first match is true? and skip all data of rest dates?
OR
Is there any other way to do this?
Please Help
Usually one would just work with LIMIT, which is not applicable here, since there might be many rows per day. What I do is quite similar to LIMIT.
SELECT * FROM (
SELECT
tg.*,
#gn := IF(DATE(tg.added_date) != #prev_date, #gn + 1, #gn) AS my_group_number,
#prev_date := DATE(tg.added_date)
FROM tblgallery tg
, (SELECT #gn := 0, #prev_date := CURDATE()) var_init
ORDER BY FIELD(DATE(tg.added_date), CURDATE() - INTERVAL 1 DAY, CURDATE() - INTERVAL 3 DAY, CURDATE() - INTERVAL 2 DAY) DESC
) sq
WHERE my_group_number = 1;
Here's how it works.
With this line
, (SELECT #gn := 0, #prev_date := CURDATE()) var_init
the variables are initialized.
Then the ORDER BY is important! The FIELD() function sorts the rows from 2 days ago (gets value 3), to 3 days ago (gets value 2), to 1 day ago (gets value 1). Everything else gets value 0.
Then in the SELECT clause the order is also important.
With this line
#gn := IF(DATE(tg.added_date) != #prev_date, #gn + 1, #gn) AS my_group_number,
the variable #gn is incremented when the date of the current row is different from the date of the previous row.
With this line
#prev_date := DATE(tg.added_date)
the date of the current row is assigned to the variable #prev_date. In the line above it still has the value of the previous row.
Now those entries have a 1 in column my_group_number that have the most recent date in the order
2 days ago
3 days ago
yesterday
4 days ago
5 days ago
...
Try this Query:
SELECT GalleryID, PixName, A.added_date
FROM tblGallery A
INNER JOIN (
SELECT added_date FROM tblGallery
WHERE added_date <= DATE_SUB('2014-07-09 00:00:00', interval 2 day)
GROUP BY added_date
ORDER BY added_date DESC
LIMIT 1 ) B
ON A.added_date = B.added_date
See my SQL Fiddle Demo
And even if the date is more than 2 days older it will still work.
See here the Demo below wherein the latest is 4 days older from July 9, 2014
See the 2nd Demo
And if you want the current date instead of literal date like here then you could use CURDATE() function instead. Like one below:
SELECT GalleryID, PixName, A.added_date
FROM tblGallery A
INNER JOIN (
SELECT added_date FROM tblGallery
WHERE added_date <= DATE_SUB(CURDATE(), interval 2 day)
GROUP BY added_date
ORDER BY added_date DESC
LIMIT 1 ) B
ON A.added_date = B.added_date
See 3rd Demo
Well, I'm not being able to solve the multi OR issue but this is how could you get records being added last two days. Change the interval or the CURDATE() in order to fit your needs.
SELECT id, date_added
FROM gallery
WHERE date_added BETWEEN CURDATE() - INTERVAL 2 DAY AND CURDATE()
ORDER BY date_added
Check the SQL Fiddel
It is not about how OR works in MySQL.
I think you are misunderstanding where part by looking at your discussion with #B.T.
It will be executed for each record.
so if one of the record evaluates to false for the first condition then it will evaluate the second condition for that particular record and so on so if any condition evaluates to true by considering all the conditions then that will become part of your result set.
Try this query.
SELECT `tg`.* FROM `tblgallery` AS `tg` WHERE tg.added_date = (
select date (
select distinct(tg.added_date) date from tblgallery as tg
) as t1 order by case
when date between '2014-07-07 00:00:00' AND '2014-07-08 00:00:00'
then 1
when date between '2014-07-06 00:00:00' AND '2014-07-07 00:00:00'
then 2
when date between '2014-07-08 00:00:00' AND '2014-07-09 00:00:00'
then 3
else 4
end limit 1);
Here's what I am doing in this query.
I am getting all the distinct dates.
then I am ordering all the condition in order i.e if first condition is true then 1, if second is true then 2 and so on.
I am limiting the result to 1 so after the order whichever the result is the first row will be selected and which is a date and will be used in the condition.
Note: I have note tested it yes, so you may need to do some changes to the query.
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)