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.
Related
I really need some help. Not MySQL friendly, muddled through this last few days but now stuck...
Need to take the below query and modify it to pull out only records closed in month of "January" for instance. Is this possible from the below? Cant figure it...
<?php
$recentlyClosedDays = 7;
?>
$query1 = "
SELECT HD_TICKET.ID as ID,
HD_TICKET.TITLE as Title,
HD_STATUS.NAME AS Status,
HD_PRIORITY.NAME AS Priority,
HD_TICKET.CREATED as Created,
HD_TICKET.MODIFIED as Modified,
S.FULL_NAME as Submitter,
O.FULL_NAME as Owner,
HD_TICKET.RESOLUTION as Resolution,
(SELECT COMMENT FROM HD_TICKET_CHANGE WHERE HD_TICKET_ID=HD_TICKET.ID ORDER BY TIMESTAMP DESC LIMIT 1) as Comment,
HD_TICKET.CUSTOM_FIELD_VALUE0 as Type
FROM HD_TICKET
JOIN HD_STATUS ON (HD_STATUS.ID = HD_TICKET.HD_STATUS_ID)
JOIN HD_PRIORITY ON (HD_PRIORITY.ID = HD_TICKET.HD_PRIORITY_ID)
LEFT JOIN USER S ON (S.ID = HD_TICKET.SUBMITTER_ID)
LEFT JOIN USER O ON (O.ID = HD_TICKET.OWNER_ID)
WHERE (HD_TICKET.HD_QUEUE_ID = $mainQueueID)
AND (HD_STATUS.STATE like '%Closed%')
AND (HD_TICKET.TIME_CLOSED >= DATE_SUB(NOW(), INTERVAL $recentlyClosedDays DAY))
ORDER BY HD_TICKET.TIME_CLOSED DESC
";
Any help would be greatly apprecaited and beer will be owed :)
To select DATE, DATETIME, or TIMESTAMP values in the current month, you do this.
WHERE timestampval >= DATE(DATE_FORMAT(NOW(), '%Y-%m-01'))
AND timestampval < DATE(DATE_FORMAT(NOW(), '%Y-%m-01')) + INTERVAL 1 MONTH
For the previous month you can do this:
WHERE timestampval >= DATE(DATE_FORMAT(NOW(), '%Y-%m-01')) - INTERVAL 1 MONTH
AND timestampval < DATE(DATE_FORMAT(NOW(), '%Y-%m-01'))
For the previous year you could do this:
WHERE timestampval >= DATE(DATE_FORMAT(NOW(), '%Y-01-01')) - INTERVAL 1 YEAR
AND timestampval < DATE(DATE_FORMAT(NOW(), '%Y-01-01'))
You can summarize (aggregate) tables by month like this:
SELECT DATE(DATE_FORMAT(timestampval , '%Y-%m-01')) AS month_starting,
SUM(whatever) AS total,
COUNT(whatever) AS transactions
FROM table
GROUP BY DATE(DATE_FORMAT(timestampval , '%Y-%m-01'))
This all works because this expression:
DATE(DATE_FORMAT(sometime, '%Y-%m-01'))
takes an arbitrary sometime value and returns the first day of the month in which the timestamp occurs. Similarly,
DATE(DATE_FORMAT(sometime, '%Y-01-01'))
returns the first day of the year. You can then use date arithmetic like + INTERVAL 1 MONTH to manipulate those first days of months or years.
Here's a more complete writeup on this topic. http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/
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 am trying to get rows witch has a season start date and season end date in current date.
But I have problem with periods between months in winter. For example winter starts 01.11 and end at 28.02 (I don't care about 27 or 28)
When I try to get products in winter like below query
SELECT *
FROM products P
LEFT JOIN seasons S ON P.s_id = S.id
WHERE MONTH(CURDATE()) BETWEENS MONTH(S.startdate) and MONTH(S.enddate)
I get nothing
The table seasons has one row with below value
id = 1
Description = Winter
startdate = 2013-11-01
enddate = 2014-02-28
!IMPORTANT
I don;t care about year
Can anyone help? thanks
You can achieve this with a CASE:
SELECT *
FROM products P
LEFT JOIN seasons S ON P.s_id = S.id
WHERE CASE
WHEN MONTH(S.startdate) > MONTH(S.enddate)
AND (MONTH(CURDATE()) > MONTH(S.startdate)
OR MONTH(CURDATE()) < MONTH(S.enddate))
THEN 1
WHEN MONTH(S.startdate) <= MONTH(S.enddate)
AND MONTH(CURDATE()) BETWEEN MONTH(S.startdate) AND MONTH(S.enddate)
THEN 1
ELSE 0
END
This assumes that whenever MONTH(startdate) > MONTH(enddate) the year has changed.
Will return true in that case whenever MONTH(curdate()) is bigger than MONTH(startdate) OR is smaller than MONTH(enddate).
In the case when MONTH(startdate) <= MONTH(enddate) it just validates if it is between them.
sqlfiddle demo
Why would you get anything? You're doing the equivalent of
WHERE 5 BETWEEN 'yyyy-mm-dd' AND 'yyyy-mm-dd'
You're doing a literal apples/oranges comparison. Perhaps you want
WHERE MONTH(CURDATE()) BETWEEN MONTH(S.startdate) AND MONTH(S.enddate)
instead, so you're doing apples to apples.
I don't like the strange converting to strings and parsing solutions. You can do this with a little math...
SELECT *
FROM products P
LEFT JOIN seasons S ON P.s_id = S.id
WHERE ((DAYOFYEAR(CurDate()) - DAYOFYEAR(S.startdate)) + 365) % 365 BETWEEN 0 AND
(((DAYOFYEAR(S.enddate) + 365) - DAYOFYEAR(S.startdate))) % 365
Between requires the values be in order - your start date is AFTER your end date so you'll always get nothing.
Additionally, you are comparing months to dates; since the date has the year in it you can't ignore the year that way.
You have to compare the day in year to ignore the year. If DAYOFYEAR(curdate()) is between DAYOFYEAR(end date) and DAYOFYEAR(start date) that will work if both end data and start date are in the same year. You'll need to get a bit fancier if they are in different years, but that should be obvious.
Also, you probably have to think through what "not caring about year" really means. If you're looking for dates between March and September, that will be the reverse of looking for dates between September and March. What you want will alter how you program it.
I found solution.
SELECT *
FROM products P
LEFT JOIN seasons S ON P.s_id = S.id
WHERE
IF(
( CURDATE() BETWEEN STR_TO_DATE( CONCAT( YEAR(CURDATE()),'-',MONTH(S.startdate),'-',DAY(S.startdate)) , '%Y-%m-%d') AND STR_TO_DATE( CONCAT( IF( MONTH(S.`enddate`) < MONTH(S.`startdate`) ,YEAR(CURDATE())+1,YEAR(CURDATE()) ),'-',MONTH(s.`enddate`),'-',DAY(S.`enddate`)) , '%Y-%m-%d') ),
"IN Season",
IF( P.`s_id` IS NOT NULL AND P.`s_id` != "","Out OF Season","" )
) = "IN Season"
I have 2 rows
1. Startdate: 2013-11-01 EndDate: 2014-02-28 (Winter)
2. StartDate: 2013-06-01 ENDDate: 2013-08-31 (Summer)
It works perfect.
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.
I'm trying to create a custom query that will show the number of stories that have been posted in the last 24 hours on a Drupal 6 site.
Stories are stored in the "node" table. each record has a "created" row that records the UNIX timestamp when the story was posted.
Here's the query I'm trying so far:
$sq = 'SELECT COUNT(*) cnt '
. 'FROM {node} c WHERE created >= dateadd(hour,-24,getdate())';
This doesn't appear to be working though. What am I doing wrong?
EDIT: Here's the overall code I'm trying to use right now:
$sq = 'SELECT COUNT(*) AS cnt FROM {NODE} n WHERE FROM_UNIXTIME(n.created) >= DATE_SUB(NOW(), INTERVAL 1 DAY)';
$q = db_query($sq);
while ($o = db_fetch_object($q)) {
print_r($o);
}
That print_r isn't returning anything. Where's my error?
For MySQL, use:
SELECT COUNT(*) AS cnt
FROM NODE n
WHERE FROM_UNIXTIME(n.created) >= DATE_SUB(NOW(), INTERVAL 1 DAY)
Mind that NOW() includes the time when the statement is run. If you want to count records, starting from midnight of the previous day, use:
SELECT COUNT(*) AS cnt
FROM NODE n
WHERE FROM_UNIXTIME(n.created) >= DATE_SUB(CURRENT_DATE, INTERVAL 1 DAY)
Reference:
FROM_UNIXTIME
DATE_SUB
Since you are doing this in PHP, you can just use $_SERVER['REQUEST_TIME']. My guess is that it will be faster than doing date manipulations with SQL:
$count = db_result(db_query("SELECT COUNT(nid) FROM {node}
WHERE created >= %d;", $_SERVER['REQUEST_TIME'] - 86400));
Alternative you could use time to get the current timestamp, but that will be a tiny bit slower than using the $_SERVER['REQUEST_TIME'] variable.