MySQL order by IF and sorting dates - mysql

In sorting the ORDER BY of dates in MySQL, I'm trying to sort ASC for dates after today, and DESC for dates before today in one go.
I'm doing something similar to:
SELECT * FROM test ORDER BY IF(created >= NOW(), created, 1) ASC, created DESC
This gives me a somewhat desired result:
ID CREATED
8 May, 10 2014 11:15:00+0000
7 May, 03 2014 20:47:00+0000
5 May, 02 2014 14:00:00+0000
4 April, 30 2014 17:41:00+0000
3 April, 30 2014 17:00:00+0000
1 April, 21 2014 03:30:00+0000
6 March, 23 2014 12:00:00+0000
9 May, 20 2014 20:45:00+0000
10 July, 02 2014 20:30:00+0000
2 June, 30 2015 11:16:00+0000
Before today is DESC, after today is ASC. However, I want to see the block of ID's 9,10,2 at the top of the results.
Any thoughts on how to do this is appreciated.
Test Link: http://www.sqlfiddle.com/#!2/4667b/2/0

Add an additional condition to the order by, which is a binary indicator of whether the date is in the past or future:
SELECT *
FROM test
ORDER BY (created >= NOW()) desc,
IF(created >= NOW(), created, NULL) ASC,
created DESC;

how about splitting it up into 2 sql queries and then unioning the results?
(SELECT * FROM test WHERE created >= NOW() ORDER BY created ASC)
UNION
(SELECT * FROM test WHERE created < NOW() ORDER BY created DESC)

Related

MySQL query to see changes during the month of last 3 month

I want to see changes during the month of last 3 month.
Database:
MySQL:
SELECT DATE_FORMAT(CUR.time, '%Y %b') AS thisTime,
MAX(CUR.value) AS thisValue,
MAX(PRE.value) AS prevValue,
MAX(CUR.value)-MAX(PRE.value) AS compValue
FROM test CUR
INNER JOIN test PRE
ON MONTH(PRE.time) = (SELECT MAX(MONTH(XXX.time))
FROM test XXX
WHERE MONTH(XXX.time) < MONTH(CUR.time))
WHERE CUR.time > DATE_SUB(NOW(), INTERVAL 3 MONTH)
GROUP BY MONTH(CUR.time)
ORDER BY CUR.id
Result:
Can see that there is no January!
I want to see changes during the month of last 3 month like this.
Anyone got ideas?
| 2019 Feb | 50 | 40 | 10 |
| 2019 Jan | 40 | 25 | 15 |
| 2018 Dec | 25 | 20 | 5 |
The problem with trying to use MONTH in a JOIN condition is the wrap around from 12 to 1 at the end of a year. It's easiest just to use the date as a YEARMONTH string:
SELECT DATE_FORMAT(t1.time, '%b %Y') AS thisTime,
MAX(t1.value) AS thisValue,
MAX(t2.value) AS prevValue,
MAX(t1.value) - MAX(t2.value) AS compValue
FROM test t1
JOIN test t2 ON DATE_FORMAT(t2.time, '%Y%m') = (SELECT MAX(DATE_FORMAT(`time`, '%Y%m'))
FROM test t3
WHERE DATE_FORMAT(t3.time, '%Y%m') < DATE_FORMAT(t1.time, '%Y%m'))
GROUP BY thisTime
ORDER BY STR_TO_DATE(CONCAT('1 ', thisTime), '%d %b %Y') DESC
Output:
thisTime thisValue prevValue compValue
Feb 2019 50 40 10
Jan 2019 40 25 15
Dec 2018 25 20 5
Demo on dbfiddle
Instead of trying a complicated join, you can just use sub-queries since the table isn't very complicated. Also just filter for the results on the 2nd of every month since you don't need the 1st from what your expected result shows.
SELECT
DATE_FORMAT(cur.time,'%Y %b') AS thisTime,
cur.value AS thisValue,
(SELECT value FROM test WHERE time = DATE_SUB(cur.time,INTERVAL 1 MONTH)) AS prevValue,
(SELECT cur.value - value FROM test WHERE time = DATE_SUB(cur.time,INTERVAL 1 MONTH)) AS compValue
FROM
test AS cur
WHERE
cur.time >= DATE_SUB(CURDATE(),INTERVAL 3 MONTH)
AND DATE_FORMAT(cur.time,'%d') = 2
Result:
thisTime thisValue prevValue compValue
2019 Feb 50 40 10
2019 Jan 40 25 15
2018 Dec 25 20 5
EDIT
Per Nick's point below, I did assume that the max would always be the 2nd of every month. If that's false and the table snapshot just doesn't happen to show a month where the 1st was the max, then this modification will give the correct max result.
The JOIN of monthMax and the regular test table will guarantee you always pull the max value per month, no matter how many dates there are within that month. Then just use a similar sub-query method as my initial query.
SELECT
DATE_FORMAT(t.time,'%Y %b') AS thisTime,
t.value AS thisValue,
(SELECT MAX(value) FROM test WHERE DATE_FORMAT(time,'%Y-%m') = DATE_FORMAT(DATE_SUB(t.time,INTERVAL 1 MONTH),'%Y-%m')) AS prevValue,
(SELECT t.value - MAX(value) FROM test WHERE DATE_FORMAT(time,'%Y-%m') = DATE_FORMAT(DATE_SUB(t.time,INTERVAL 1 MONTH),'%Y-%m')) AS compValue
FROM
(SELECT DATE_FORMAT(time,'%Y-%m') AS 'timeFix', MAX(value) AS 'maxValue' FROM test GROUP BY timeFix) AS monthMax
JOIN (test AS t) ON (monthMax.maxValue = t.value AND monthMax.timeFix = DATE_FORMAT(t.time,'%Y-%m'))
WHERE
t.time > DATE_SUB(CURDATE(),INTERVAL 3 MONTH)
Result:
thisTime thisValue prevValue compValue
2019 Feb 50 40 10
2019 Jan 40 25 15
2018 Dec 25 20 5

Get Mondays of the last two months from SQL Server 2008

If today is 10 JULY 2015 then what I need is a record set in SQL Server which has the following records
6 JULY 2015
29 JUNE 2015
22 JUNE 2015
15 JUNE 2015
8 JUNE 2015
1 JUNE 2015
25 MAY 2015
With the following expression you get the last Monday:
CAST(GETDATE()-(##DATEFIRST-1+DATEPART(weekday,GETDATE())) % 7+1 AS DATE)
Here we use ##DATEFIRST and DATEPART(weekday,...) to find the last MONDAY.
Then you should generate 8 (two months) rows sequence of days each of them -7 days prior the next. Here I've used CTE but you can use UNION or TOP 8 rows from existing (system or user) in your DB table to generate 8 rows.
;WITH CT(n) AS
(
SELECT 0
UNION ALL
SELECT n+1 FROM CT WHERE n < 7
)
SELECT DATEADD(DAY,-n*7,
CAST(GETDATE()-
(##DATEFIRST-1+DATEPART(weekday,GETDATE())) % 7+1 AS DATE)) as DT
FROM CT ORDER BY n
Sqlfiddle demo
Try this
;WITH dates AS
(
SELECT CONVERT(datetime,cast(month(getdate())-2 as varchar(2))+'/'+cast(day(getdate()) as varchar(2))+'/'+ cast(year(getdate()) as varchar(4))) as Date,' ' as eid
UNION ALL
SELECT DATEADD(d,1,[Date]),' ' as eid
FROM dates
WHERE DATE < GETDATE()
)
select datename(DD,dates.date)+' '+datename(MM,dates.date)+' '+ datename(YYYY,dates.date) from dates
where datename(dw,dates.date) = 'Monday'

SQL order by clause for 2 date columns

I have a table with a column named timestamp
i want to be able to order by month(timestamp) and year(timestamp) and group the months and years together
so for example, if i had the following timestamps:
2014-01-01
2014-02-01
2014-05-01
2015-01-01
i want to show in this order
MONTH YEAR
1 2015
5 2014
2 2014
1 2014
You can pick month and year from timestamp with MONTH and YEAR:
GROUP BY MONTH(field_with_ts) , YEAR(field_with_ts)
and the same thing with the ORDER BY clause.
Try this:
substring(date, 8 , 2) as Month, substring(date, 1, 3) as Year
See, if that works.

Table Date column comparison with a generated dates list - MYSQL

There seem to be a weird behaviour when comparing a date in table column with a date generated in a list within MYSQL.
Please take a look at the * SQLFIDDLE reference.
Payroll Table:
ID DATESTAMP
1 August, 30 2012 00:00:00+0000
2 September, 02 2012 00:00:00+0000
3 September, 15 2012 00:00:00+0000
4 September, 24 2012 00:00:00+0000
5 October, 05 2012 00:00:00+0000
6 October, 16 2012 00:00:00+0000
7 October, 19 2012 00:00:00+0000
8 November, 02 2012 00:00:00+0000
9 November, 10 2012 00:00:00+0000
10 November, 16 2012 00:00:00+0000
11 November, 24 2012 00:00:00+0000
12 November, 30 2012 00:00:00+0000
13 December, 01 2012 00:00:00+0000
14 December, 07 2012 00:00:00+0000
Dates list is generated between two particular dates with a constant day interval
Query:
set #i:= 0;
SELECT date_format(DATE(ADDDATE('2012-10-05',
INTERVAL #i:=#i+14 DAY)),'%Y-%m-%d')
AS dateP, #i
FROM payroll
HAVING #i < datediff(now(), date '2012-10-05')
;
DATEP #IntervalDays
2012-10-19 14
2012-11-02 28
2012-11-16 42
2012-11-30 56
2012-12-14 70
As you can see the generated dates list has matches to the Payroll table above. However when the comparison is done, it reutns zero records.
Comparison Query:
set #i:= 0;
SELECT distinct datestamp FROM payroll
WHERE date(datestamp) in (
SELECT DATE(ADDDATE('2012-10-05',
INTERVAL #i:=#i+14 DAY) ) AS dateP
FROM payroll
where #i < DATEDIFF(now(), date '2012-10-05')
)
;
So Questions I have:
Is the inner query stop generating dates when used as a nested query?
Is there anything wrong with the dates comparison method I am using here?
What could be the reason for this entire failure?
How to fix it within Select itself without any procedure/functions? :)
PS:
I am also trying to test this in SQL server as well as Oracle.
There are many good questions and answers to support 'Date Comparison' issues occurred at various scenarios with the site. That includes posts such as mysql date comparison with date_format. etc.. May be there's one hidden somewhere asking for exact issue I am facing with different wording. Couldn't find and hence posted the question.
Second UPDATE:
Now I got it working in every version:
select
*
from
Payroll
inner join
(
SELECT DATE(DATE_ADD('2012-10-05',
INTERVAL #i:=#i+14 DAY) ) AS dateP
FROM Payroll, (SELECT #i:=0) r
where #i < DATEDIFF(now(), date '2012-10-05')
) sq on Payroll.datestamp = sq.dateP
You just have to initialize the variable inside the query.
UPDATE:
Strange thing is, this one works on my local machine without problems (version 5.1.41-3ubuntu12.7-log), but not in your SQLfiddle.
set #i:= 0;
select
*
from
Payroll
inner join
(
SELECT DATE(DATE_ADD('2012-10-05',
INTERVAL #i:=#i+14 DAY) ) AS dateP
FROM Payroll
where #i < DATEDIFF(now(), date '2012-10-05')
) sq on Payroll.datestamp = sq.dateP
END OF UPDATE
Have you tried it like this?
set #i:= 0;
SELECT distinct datestamp FROM payroll
WHERE STR_TO_DATE(datestamp, '%M, %d %Y %H:%i:%f') in (
SELECT DATE(ADDDATE('2012-10-05',
INTERVAL #i:=#i+14 DAY) ) AS dateP
FROM payroll
where #i < DATEDIFF(now(), date '2012-10-05')
)
;
My guess is, that the DATE() function fails, cause you're varchar(is it?) date is not in ISO format. Therefore you have to use STR_TO_DATE() function.
For exact usage of STR_TO_DATE() read here and here. I'm not sure about the microsecond part.

MySQL select all last added query

Im trying to select all the items that are recently added.
I have to find what is the last added exam (sometimes more that one as shown below)
Here is an example of the DB.
id name start_date end_date duration
4 Exam August 2011 24.8.2011 0000-00-00 20
3 Exam July 2011 28.7.2011 0000-00-00 20
5 Exam August 2011 24.8.2011 0000-00-00 20
6 Exam August 2011 24.8.2011 0000-00-00 20
25 Exam September 2011 26.9.2011 07.10.2011 20
26 Exam September 2011 26.9.2011 07.10.2011 20
27 Exam September 2011 26.9.2011 07.10.2011 20
And here is the query that im using, my question is is there a better way to do it? Some optimization?
SELECT * FROM me_tests WHERE name = (
SELECT name FROM me_tests ORDER BY start_date DESC LIMIT 1
)
The second query will find the name of the last added (same as start date) one, will pass it to the first select and will select all the other tests based on the name.
SELECT * FROM me_tests ORDER BY start_date DESC LIMIT 1
Or -
SELECT * FROM me_tests
WHERE start_date =
(SELECT MAX(start_date) FROM me_tests);
What if the last added items were added at the same time but have different names?
In that case I would think you would want a query like this.
SELECT
*
FROM me_tests
WHERE start_date = (SELECT
start_date
FROM me_tests
ORDER BY start_date DESC LIMIT 1)
I would use
SELECT * FROM me_tests ORDER BY start_date DESC LIMIT 1
... except using * is bad, so you should name all the columns you want to retrieve.