Is it possible to use a resulting "column" from an if-condition in another if-condition inside the same query? I'd like to get all my documents related data once.
SELECT d.id, d.filename,
IF(d.document_valid_until_further_notice = 0,
IF(d.document_valid_until = "0000-00-00",
DATE_ADD(d.document_date, INTERVAL 12 MONTH),
d.document_valid_until
), "0000-00-00"
) AS calculated_valid_until_date,
IF(calculated_valid_until_date != "0000-00-00",
DATE_SUB(calculated_valid_until_date, INTERVAL 8 WEEK,
"0000-00-00"
) AS calculated_alert_expiring_date,
IF(calculated_valid_until_date > CURDATE() AND calculated_valid_until_date != "0000-00-00", 1, 0) AS expired FROM documents AS d WHERE 1 ORDER BY d.document_date DESC';
Now, "calculated_valid_until_date" comes out correctly, e.g. 2015-10-20, but I can't use that value in the following if-statements. with or without # -sign. (#calculated_valid_until_date). Is there even a way or do I have to do this all with separated queries or in client side?
Thanks for any ideas!
Wrap the calculated fields of the into a derived table, then reference these calculations in an outer select from this derived table to continue your calculations:
SELECT x.id, x.filename,
x.calculated_valid_until_date,
x.calculated_alert_expiring_date,
-- Use the derived table to do the second round of calculations
IF(x.calculated_valid_until_date > CURDATE()
AND x.calculated_valid_until_date != "0000-00-00", 1, 0) AS expired
FROM
-- Project the first round of calculations into a derived table
(SELECT
d.id, d.filename, d.document_date,
IF(d.document_valid_until = '0000-00-00', DATE_ADD(d.document_date, INTERVAL 12 MONTH),
d.document_valid_until) AS calculated_valid_until_date,
IF(d.calculated_valid_until_date != '0000-00-00',
DATE_SUB(d.calculated_valid_until_date, INTERVAL 8 WEEK),
'0000-00-00') AS calculated_alert_expiring_date
FROM documents AS d
) x
WHERE 1 = 1
ORDER BY x.document_date DESC;
SqlFiddle here
SELECT x.id, x.filename,
x.calculated_valid_until_date,
IF(x.calculated_valid_until_date < CURDATE() AND x.calculated_valid_until_date != "0000-00-00", 1, 0) AS expired, IF(x.calculated_valid_until_date != '0000-00-00',
DATE_SUB(x.calculated_valid_until_date, INTERVAL 8 WEEK), '0000-00-00') AS calculated_alert_expiring_date
FROM
(SELECT
d.id, d.filename, d.document_date, d.calculated_alert_expiring_date,
IF(d.document_valid_until = '0000-00-00', DATE_ADD(d.document_date, INTERVAL 12 MONTH),
d.document_valid_until) AS calculated_valid_until_date
FROM documents AS d
) x
WHERE 1 = 1
ORDER BY x.document_date DESC
CREATE TABLE documents
(
id INT,
filename VARCHAR(50),
document_valid_until_further_notice TINYINT(1),
document_valid_until_date DATE,
document_date DATE,
document_valid_until DATE,
calculated_valid_until_date DATE,
calculated_alert_expiring_date DATE
);
This is how I got it working. I didn't look at the fiddle the first time so I didn't quite realize I'd have to create the table columns for "calculated_valid_until_date" and "calculated_alert_expiring_date".
But anyhow, it is working just perfect now. THANK YOU!
Related
I've written this SQL query but it's returning null although it shouldn't
I'm working with a db containing work times. So each work times has a beginning datetime and an end datetime
I'd like to get all the work times that have been recorded this month :
SELECT id, DATE_FORMAT(DATE(t.begin), "%d-%m-%Y") as date,
CONCAT(DATE_FORMAT(t.begin, "%H:%i"), " - ", DATE_FORMAT(t.end, "%H:%i")) as timerange,
CASE WHEN t.end = "0000-00-00 00:00:00"
THEN TIME_FORMAT(TIMEDIFF(DATE_ADD(NOW(), INTERVAL 1 HOUR), t.begin), "%H:%i")
ELSE TIME_FORMAT(TIMEDIFF(t.end, t.begin), "%H:%i")
END as fulltime_duration,
(
SELECT TIMEDIFF(l.end, l.begin) as duration
FROM timeslots l
WHERE l.type = 2
AND l.parent_id = t.id
) as lunch_duration
FROM timeslots t
WHERE t.user_id = '.$userId.'
AND t.approved = 1
AND t.type = '.Timeslot::DAY.'
AND DATE(begin) >= DATE_FORMAT(CURDATE(), "%Y-%m-01")
ORDER BY t.begin
when I replace this line :
AND DATE(begin) >= DATE_FORMAT(CURDATE(), "%Y-%m-01")
by :
AND WEEKOFYEAR(begin) = WEEKOFYEAR(NOW())
it returns all the times recorded this week. So everything is working except this line
AND DATE(begin) >= DATE_FORMAT(CURDATE(), "%Y-%m-01")
When I replace the "-01" by "%d" it returns all the work times of today so I guess the problem comes from this "-01". But I don't see how to do this in a different way.
Anyone to help me ?...
Here is one way:
DATE(begin) >= DATE_ADD(CURDATE(), 1 - DAY(CURDATE()))
Note: You don't need DATE(begin) (because the time component will make this bigger), so you can use:
begin >= DATE_ADD(CURDATE(), 1 - DAY(CURDATE()))
The advantage is that this can make use of an index on begin, if appropriate.
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 the below table:
studentid VARCHAR(12)
latetime DATETIME
attendance CHAR(1)
latetime only have weekdays.
Some of the days the students will have "Parents letter" indicated by V for attendance column.
I need to group these attendance column V by consecutive week days.
Then count these occurrences.
Each group of consecutive days are counted as 1 letter.
My SQLFIDDLE: http://sqlfiddle.com/#!2/55d5b/1
This SQLFIDDLE sample data should return
STUDENTID LETTERCOUNT
a1111 3
b2222 2
a1111 - 3 counts
-----
1. 2014-01-02
2. 2014-01-27
2. 2014-01-29 and 2014-01-30
b2222 - 2 counts
-----
1. 2014-01-02 and 2014-01-03
2. 2014-01-24, 2014-01-27 and 2014-01-28
I tried various methods from the below SO without any proper result yet:
How to GROUP BY consecutive data (date in this case)
MySQL: group by consecutive days and count groups
I can do this programatically in PHP by looping through the results and manually checking for each record + its next date. But i was trying to acheive the same with SQL.
Any help / direction towards finding a solution will be much appreciated.
This is derived from one of the answers in MySQL: group by consecutive days and count groups. I added the WITH ROLLUP option to get the letter count into the same query, and used GROUP_CONCAT to show all the dates. I made the INTERVAL conditional on the weekday, to skip over weekends; holidays aren't taken into account, though.
In my version of the fiddle I changed the latetime column to date, so I could remove all the DATE() functions from the SQL.
SELECT studentid, IFNULL(dates, '') dates, IF(dates IS NULL, lettercount, '') lettercount
FROM (
SELECT studentid, dates, COUNT(*) lettercount
FROM (
SELECT v.studentid,
GROUP_CONCAT(latetime ORDER BY latetime SEPARATOR ', ') dates
FROM
(SELECT studentid, latetime,
#start_date := IF(#last_student IS NULL OR #last_student <> studentid,
1,
IF(#last_latetime IS NULL
OR (latetime - INTERVAL IF(WEEKDAY(latetime) = 0, 3, 1) DAY) > #last_latetime, latetime, #start_date)) AS start_date,
#last_latetime := latetime,
#last_student := studentid
FROM
studentattendance, (SELECT #start_date := NULL, #last_latetime := NULL, #last_student := NULL) vars
WHERE attendance = 'V'
ORDER BY
studentid, latetime
) v
GROUP BY
v.studentid, start_date) x
GROUP BY studentid, dates WITH ROLLUP) y
WHERE studentid IS NOT NULL
ORDER BY studentid, dates
http://sqlfiddle.com/#!2/6c944/12
Iām trying to figure out if a shop is currently within its opening hours, if not then select the next time its open.
Finally I need to be able to put the opening day as a specific date.
Can someone possible give me a tip how to construct this query?
Thanks in advance
CREATE TABLE `shop_hours` (
`id` int(11) NOT NULL,
`shop_id` int(11) unsigned NOT NULL,
`day_of_week` int(11) unsigned NOT NULL,
`open_time` time NOT NULL,
`close_time` time NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `shop_hours` (`id`, `shop_id`, `day_of_week`, `open_time`, `close_time`)
VALUES
(1, 1, 0, '08:00:00', '24:00:00'),
(2, 1, 1, '08:00:00', '24:00:00'),
(3, 1, 2, '08:00:00', '24:00:00'),
(4, 1, 3, '08:00:00', '24:00:00'),
(5, 1, 4, '08:00:00', '24:00:00'),
(6, 1, 5, '08:00:00', '24:00:00'),
(7, 1, 6, '08:00:00', '24:00:00');
Edit:
To clarify a little I'm not looking to find open shops, but only open hours for ONE specific shop. Based on the opening/closing hour, and what time it is now. I will generate some selectable timestamps incremented by 15 minutes.
E.g. if a shop has just closed (1PM), I will need to use the next open day's open/closing time instead. (the shop isn't necessarily open every day, could be closed Sundays).
To find out shop_id's, that is open for NOW()
SELECT *
FROM `shop_hours`
WHERE `day_of_week` = DATE_FORMAT(NOW(), '%w')
AND CURTIME() BETWEEN `open_time` AND `close_time`
Obsolete:
To find tomorrow's available open_times:
SELECT *
FROM `shop_hours`
WHERE `day_of_week` = DATE_FORMAT(DATE_ADD(NOW(), INTERVAL 1 DAY), '%w')
Edit 2:
To find next available open_times:
SELECT `shop_id`,
MIN(CAST(CONCAT(DATE(DATE_ADD(NOW(), INTERVAL ((7 + DATE_FORMAT(NOW(), '%w') - `day_of_week`) % 7) DAY)), ' ', `open_time`) AS DATETIME)) AS `next_open_datetime`
FROM `shop_hours`
GROUP BY `shop_id`
Edit:
DATE_FORMAT(*DATE*, '%w') uses the format 0 = Sunday ... 6 = Saturday
If you want to use the ISO format 1 = Monday ... 7 = Sunday in your day_of_week field, you should bind php's date('N') to your query (or use Mysql's if function IF(DATE_FORMAT(NOW(), '%w') = 0, 7, DATE_FORMAT(NOW(), '%w')), but that's ugly)
1) Check if shop is open. Result is empty, if shop is closed:
select * from shop_hours
where shop_id = $id
and dayofweek(curdate()) = day_of_week
and curtime() between open_time and close_time;
2) Find next open time:
(select open_time from shop_hours
where shop_id = $id and curtime() < open_time
and day_of_week >= dayofweek(curdate()))
union
(select open_time from shop_hours
where shop_id = $id and curtime() < open_time
order by day_of_week)
union
(select open_time from shop_hours
where shop_id = $id and curtime() > close_time
and day_of_week >= dayofweek(curdate()))
union
(select open_time from shop_hours
where shop_id = $id and curtime() > close_time
order by day_of_week)
limit 1;
Untested, but this should respect weekend wraparound and holes in the week (i.e. closed days).
Keep in mind, that dayofweek() numbers 1 = Sunday, 2 = Monday, ... If your table stores the weekdays in a different format, you must adjust the query accordingly.
This is the toughest query I ever made:
http://robertr.pastebin.com/X4bG4pFp
"SELECT `user`.`id` , `user`.`fname` , `user`.`lname` ,
YEAR( `user`.`bday` ) AS `bday_year` , `user`.`class_id` ,
( SELECT `class`.`class_name`
FROM `wp_class_classes` `class`
WHERE `user`.`class_id` = `class`.`id`) AS `class_name`
FROM `wp_class_users` `user`
WHERE MONTH( `bday` ) = $month AND DAY( `bday` ) = $day
OR `user`.`fname` =
( SELECT `name`.`names`
FROM `wp_class_namedays` `name`
WHERE `name`.`day` = '$month.$day'
AND `user`.`fname` = `name`.`names` )
This query grabs data from three different database tables to check if there is someone in the database, who has a party today. And in Latvia we have Name Days too. Anyway, this query works well, and does its job well, but now I want to make it a bit cooler.
I want it to show, who will be having a party next week. You've probably noticed these emails that Facebook sends to you every weekend showing who has a birthday coming up.
But I just can't understand how to get at least that interval?
I remember that PHP has some good functions with which you can find on which day starts month and so on, but maybe here are some bright heart, and willing to help me kick me a bit faster forward.
SELECT
`user`.`id`,
`user`.`fname`,
`user`.`lname` ,
YEAR(`user`.`bday`) AS `bday_year`,
`user`.`class_id`,
(
SELECT
`class`.`class_name`
FROM `wp_class_classes` `class`
WHERE `user`.`class_id` = `class`.`id`
) AS `class_name`,
CASE
WHEN MONTH(`week`.`Date`) = MONTH(`user`.`bday`) AND
DAY(`week`.`Date`) = DAY(`user`.`bday`) THEN 1
ELSE 2
END AS `event_type`
FROM `wp_class_users` `user`
LEFT JOIN `wp_class_namedays` `name` ON `user`.`fname` = `name`.`names`
LEFT JOIN (
SELECT CURDATE() + INTERVAL (1 - DAYOFWEEK(CURDATE())) DAY AS `Date` UNION ALL
SELECT CURDATE() + INTERVAL (2 - DAYOFWEEK(CURDATE())) DAY UNION ALL
SELECT CURDATE() + INTERVAL (3 - DAYOFWEEK(CURDATE())) DAY UNION ALL
SELECT CURDATE() + INTERVAL (4 - DAYOFWEEK(CURDATE())) DAY UNION ALL
SELECT CURDATE() + INTERVAL (5 - DAYOFWEEK(CURDATE())) DAY UNION ALL
SELECT CURDATE() + INTERVAL (6 - DAYOFWEEK(CURDATE())) DAY UNION ALL
SELECT CURDATE() + INTERVAL (7 - DAYOFWEEK(CURDATE())) DAY
) `week`
ON CONCAT(MONTH(`week`.`Date`), '.', DAY(`week`.`Date`)) IN (
CONCAT(MONTH(`user`.`bday`), '.', DAY(`user`.`bday`)),
`name`.`day`
)
WHERE `week`.`Date` IS NOT NULL
The user table is joined with the name day table, and the result set is then compared against the dates of the current week. The final result set lists only those users whose birthdays or name days happen during the week.
If you want to know about the events of, for example, the next week, you can simply change the intervals in the week.Date definitions as 8 - DAYOFWEEK..., 9 - DAYOFWEEK... etc.
One last thing is, instead of the correlated subquery in the select list you could use INNER JOIN, like this:
SELECT
`user`.`id`,
`user`.`fname`,
`user`.`lname` ,
YEAR(`user`.`bday`) AS `bday_year`,
`user`.`class_id`,
`class`.`class_name`
FROM `wp_class_users` `user`
INNER JOIN `wp_class_classes` `class` ON `user`.`class_id` = `class`.`id`
LEFT JOIN `wp_class_namedays` `name` ON ... /* the rest of the above script */
The event_type column as defined above can tell you whether the event is a birthday or not, but it doesn't let you know whether it's both the Birthday and a Name Day for that particular person.
In case you would like to have that distinction, you could change the event_type definition like this:
CASE
WHEN MONTH(`week`.`Date`) = MONTH(`user`.`bday`) AND
DAY(`week`.`Date`) = DAY(`user`.`bday`) THEN 1
ELSE 0
END +
CASE CONCAT(MONTH(`week`.`Date`), '.', DAY(`week`.`Date`))
WHEN `name`.`day` THEN 2
ELSE 0
END AS `event_type`
Now the result of the column would be:
1 ā a birthday
2 ā a name day
3 ā both
Additionally, you could have 'B' instead of 1 and 'N' instead of 2 (and '' instead of 0). The results would be then 'B', or 'N', or 'BN'. Not sure whether + can be used for concatenation, though. If not, put both CASEs into CONCAT().
I'm not sure if I get your query right, but the command SYSDATE() is mentioned in the MySQL docs. You might want to try something like:
... where date = SYSDATE() + 7
(check the syntax, I come from Oracle ;) )
This will get the parties for the next 7 days.