STR_TO_DATE() returns invalid date - mysql

I was given a job to create a new table in the database with correct data type. Here are sample records:
RegisteredMonthYear
------------------------
May 2011
March 1998
January 2000
Before I will insert the converted value I tried to convert it using STR_TO_DATE() to check if the values are correct and the result were exactly not I want. This is my query:
SELECT RegisteredMonthYear,
STR_TO_DATE(RegisteredMonthYear, '%M %Y') NewDate,
STR_TO_DATE(CONCAT(RegisteredMonthYear, ' 01'), '%M %Y %d') newDate2,
STR_TO_DATE(RegisteredMonthYear, '%M %Y') + INTERVAL 1 DAY newDate3
FROM TableName
+---------------------+---------------------------------+--------------------------------+----------+
| REGISTEREDMONTHYEAR | NEWDATE | NEWDATE2 | NEWDATE3 |
+---------------------+---------------------------------+--------------------------------+----------+
| May 2011 | April, 30 2011 00:00:00+0000 | May, 01 2011 00:00:00+0000 | (null) |
| March 1998 | February, 28 1998 00:00:00+0000 | March, 01 1998 00:00:00+0000 | (null) |
| January 2000 | December, 31 1999 00:00:00+0000 | January, 01 2000 00:00:00+0000 | (null) |
+---------------------+---------------------------------+--------------------------------+----------+
see here for demo: http://www.sqlfiddle.com/#!2/89a67/7
As you can see, column NEWDATE is one day behind. Why are the result like this?
When I tried to concatenate 01 in the string in column NEWDATE2 the result is as expected. Going back on NEWDATE column, I tried to add one day thinking that it will give exact value in column NEWDATE3 but the result is NULL.
Any idea about this?

You can use following formula (SQLFiddle):
SELECT date(str_to_date(RegisteredMonthYear, '%M %Y'))
+ interval 1 day
FROM tablename
I have added extra DATE() call on top of STR_TO_DATE() - but it makes all the difference.
But in general I agree that this is one more really weird MySQL gotcha.
For example, in PostgreSQL, you don't need to add 1 day and you don't need extra casts, simple to_timestamp is enough:
SELECT to_timestamp('May 2011', 'Mon YYYY');
2013-05-01 00:00:00-07

Related

Mysql query specific output [duplicate]

This question already has answers here:
mySQL Query for summing amount in columns (months)
(3 answers)
Closed 2 years ago.
I'm trying to get an output such as:
Year | January | February | March | April | May | June | July | August | September | October | November | December
Year | January | February | March | April | May | June | July | August | September | October | November | December
..and so on.
Each month column should have its own SUM(price) for that Month and the Year in question. Wherever comes blank (for example, maybe 2020 March is empty) should be blank/zero.
Query so far:
SELECT Year(date) AS y, monthname(date) AS m, SUM(price) AS p
FROM orders GROUP BY monthname(date), Year(date) ORDER BY date
Its returning as this:
2020 January someSum
2020 February anotherSum
...etc
Any way to make it return the results as above left to right rather than up-down or am I just that tired and need to go to sleep? I feel I'm confusing something horribly. Someone clarify it for me or chastise me mightly.
You can use conditional aggregation:
select
year(date) year_date,
sum(case when month(date) = 1 then price end) January,
sum(case when month(date) = 2 then price end) February,
sum(case when month(date) = 3 then price end) March,
...
from orders
group by year(date)
order by year_date

select range of dates in mysql [duplicate]

I have a data set of projects. The projects change status from beginning to end, and the date of the status change is logged in a table (table is named "events" - not my choice). Would look like this (simplified):
Date Status
2015-06-01 Start
2015-06-03 Stage 2
2015-06-07 Stage 3
In any given date range (to be determined dynamically) I want to be able to see which projects are at which status. However, using BETWEEN or other query against the data will only pull those projects whose status changed during that period, not the ones that are still at a given status.
I've currently created a very clunky solution in Excel which copies rows into new rows between status change dates, like so:
Date Status
2015-06-01 Project start
2015-06-02 Project start (copied)
2015-06-03 Stage 2
2015-06-04 Stage 2 (copied)
2015-06-05 Stage 2 (copied)
2015-06-06 Stage 2 (copied)
2015-06-07 Stage 3
This solution allows me to query the status for the project on, say, 2015-06-06 and see that it is still at Stage 2.
Is there some way I can use mySql to pull this same data, but as output to a query? I've heard some suggest to use a Calendar table, but I'm not sure how that would work. I've also seen someone recommend a Cross Join, but again, I couldn't understand from the description how that would work.
Thanks in advance for your help!
plan
create calendar table by cross joining digits and date_add over calendar period..
join your data to calendar source with date <= calendar date
take max of date <= calendar date
join back to original data source to get status
setup
drop table if exists calendar_t;
CREATE TABLE calendar_t (
id integer primary key auto_increment not null,
`date` date not null,
day varchar(9) not null,
month varchar(13) not null,
`year` integer not null
);
drop view if exists digits_v;
create view digits_v
as
select 0 as n
union all
select 1
union all
select 2
union all
select 3
union all
select 4
union all
select 5
union all
select 6
union all
select 7
union all
select 8
union all
select 9
;
insert into calendar_t
( `date`, day, month, `year` )
select
date_add('2015-01-01', interval 100*a2.n + 10*a1.n + a0.n day) as `date`,
dayname(date_add('2015-01-01', interval 100*a2.n + 10*a1.n + a0.n day)) as day,
monthname(date_add('2015-01-01', interval 100*a2.n + 10*a1.n + a0.n day)) as month,
year(date_add('2015-01-01', interval 100*a2.n + 10*a1.n + a0.n day)) as `year`
from
digits_v a2
cross join digits_v a1
cross join digits_v a0
order by date_add('2015-01-01', interval 100*a2.n + 10*a1.n + a0.n day)
;
drop table if exists example;
create table example
(
`date` date not null,
status varchar(23) not null
);
insert into example
( `date`, status )
values
( '2015-06-01', 'Start' ),
( '2015-06-03', 'Stage 2' ),
( '2015-06-07', 'Stage 3' )
;
query
select cal_date, mdate, ex2.status
from
(
select cal_date, max(ex_date) as mdate
from
(
select cal.`date` as cal_date, ex.`date` as ex_date
from calendar_t cal
inner join example ex
on ex.`date` <= cal.`date`
) maxs
group by cal_date
) m2
inner join example ex2
on m2.mdate = ex2.`date`
-- pick a reasonable end date for filtering..
where cal_date <= date('2015-06-15')
order by cal_date
;
output
+------------------------+------------------------+---------+
| cal_date | mdate | status |
+------------------------+------------------------+---------+
| June, 01 2015 00:00:00 | June, 01 2015 00:00:00 | Start |
| June, 02 2015 00:00:00 | June, 01 2015 00:00:00 | Start |
| June, 03 2015 00:00:00 | June, 03 2015 00:00:00 | Stage 2 |
| June, 04 2015 00:00:00 | June, 03 2015 00:00:00 | Stage 2 |
| June, 05 2015 00:00:00 | June, 03 2015 00:00:00 | Stage 2 |
| June, 06 2015 00:00:00 | June, 03 2015 00:00:00 | Stage 2 |
| June, 07 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
| June, 08 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
| June, 09 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
| June, 10 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
| June, 11 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
| June, 12 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
| June, 13 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
| June, 14 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
| June, 15 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
+------------------------+------------------------+---------+
sqlfiddle
reference
SO mysql sequence generation
sql cross join
you dont need to create a table with all the dates. you could alter your table to give the start and end dates for each status and use a between statement.
or using your existing data.
using #datequery as the date you want to find out the status for.
Select top 1 Status from Events
where Date <= #datequery and Date
order by Date desc
returns the most recent status change before the date you are querying.
#datequery = 2015-06-06
Status
Stage 2

MYSQL select the lastest time of each date

Hi I have a mysql table and it contained something like this
HISTORY
===================
2013-07-01 16:23:43
2013-07-01 17:32:11
2013-07-01 20:44:22
2013-07-02 23:65:12
2013-07-03 10:23:32
2013:07-03 12:54:02
and etc........
for example the first three row are 2013-07-01 16:23:43, 2013-07-01 17:32:11, and
2013-07-01 20:44:22 and the lastest date in this history is 2013-07-01 20:44:22
So what I really want is.. Is there any way that I could make a selection that have a result like this
HISTORY
===================
2013-07-01 20:44:22
2013-07-02 23:65:12
2013:07-03 12:54:02
etc....
Thank You
Try this query
select date(history) as dat,
max(history)
from table1
group by dat
SQL FIDDLE
| DAT | MAX(HISTORY) |
-------------------------------------------------------------
| July, 01 2013 00:00:00+0000 | July, 01 2013 20:44:22+0000 |
| July, 02 2013 00:00:00+0000 | July, 02 2013 23:59:12+0000 |
| July, 03 2013 00:00:00+0000 | July, 03 2013 12:54:02+0000 |
Maybe it can be like that;
select Max(history) as MyMaxTimeForADay from MyTime t1 group by Date(t1.history);
you can see how is work at this link : http://sqlfiddle.com/#!2/12be2/2/0

My SQL Finding a span of dates accross rows

I am looking for some help with even knowing where to start. Essentially we have a table for clients that hold employment start dates and end dates. For annual reports we have to calculate "continuous employment" which is defined as earliest start date to last end date as long as there is not more than 21 days between one end date and the next start date.
here is an example
employee | Start Date | End Date
1 | 2012-10-1 | 2012-11-05
1 | 2012-11-08 | 2013-1-25
2 | 2012-10-1 | 2012-11-05
2 | 2012-11-30 | 2013-1-02
in the above, i would like to see employee 1 as continuously employed from 2012-10-1 to 2013-1-25
but employee 2 would have 2 separate employment lines showing continuous employment from 2012-10-1 to 2012-11-05 and a different from 012-11-30 to 2013-1-02
Thanks for the help!
The theory is similar to #mellamokb's answer, but somewhat more concise:
SELECT employee, MIN(start) start, end
FROM (
SELECT #end:=IF(employee<=>#emp AND #stt<=end+INTERVAL 21 DAY,#end,end) end,
#stt:=start start,
#emp:=employee AS employee
FROM my_table, (SELECT #emp:=NULL, #stt:=0, #end:=0) init
ORDER BY employee, start DESC
) t
GROUP BY employee, end
See it on sqlfiddle.
One way to find "continuous groups" among a set of records is to use variables to track the difference between each line and develop groupings that combine continuous ranges together. In the example below, I use three variables to track enough information for generating the groups:
#curEmployee - tracks the current employee from the previous record, and is compared with the employee on the current record to know when we've switched to a different employee, which automatically becomes another grouping
#curEndDate - tracks the last end date from the previous record, so it can be compared with the start date of the current record to see if the current record belongs in the same "group" as the previous record - that is to say, it is part of continuous employment with the previous record
#curGroup - this is the key variable which segregates the rows into separate "groups" that represent continuous employment. The logic is that a row should be considered as continuous with the previous row if and only if the following two conditions are true: the two rows have the same employee number, and the end date of the previous row is less than 21 days from the current row.
NOTE: You may want to validate the edge conditions, i.e., whether exactly 20/21/22 days apart will be considered continuous employment or not, and tweak the logic below.
Here is the sample query that calculates those groups. A couple things to take note of: the order of variable assignment matters, because they are assigned from top to bottom in the select list. We need to assign #curGroup first, so that it still has the values of #curEmployee and #curEndDate from the previous record to draw on. Secondly, the order by clause is very important to ensure that when we are comparing the previous and current record, they are the two records that are the closest to each other. If we looked at the records in a random order, they would likely end up all as separate groups.
select
e.employee, e.`start date`, e.`end date`
,#curGroup :=
case when employee = #curEmployee
and #curEndDate + INTERVAL 21 DAY >= e.`start date`
then #curGroup
else #curGroup + 1
end as curGroup
,#curEmployee := employee as curEmployee
,#curEndDate := e.`end date` as curEndDate
from
employment e
JOIN (SELECT #curEmployee := 0, #curEndDate := NULL, #curGroup := 0) r
order by e.employee, e.`start date`
Sample Result (DEMO) - notice how CURGROUP stays at 1 for the first two lines, because they are within 21 days of each other and represent continuous employment, while the last two lines get identified as separate group numbers:
| EMPLOYEE | START DATE | END DATE | CURGROUP | CUREMPLOYEE | CURENDDATE |
-------------------------------------------------------------------------------------------------------------------------------
| 1 | October, 01 2012 00:00:00+0000 | November, 05 2012 00:00:00+0000 | 1 | 1 | 2012-11-05 00:00:00 |
| 1 | November, 08 2012 00:00:00+0000 | January, 25 2013 00:00:00+0000 | 1 | 1 | 2013-01-25 00:00:00 |
| 2 | October, 01 2012 00:00:00+0000 | November, 05 2012 00:00:00+0000 | 2 | 2 | 2012-11-05 00:00:00 |
| 2 | November, 30 2012 00:00:00+0000 | January, 02 2013 00:00:00+0000 | 3 | 2 | 2013-01-02 00:00:00 |
Now that we've established groups of records that are part of continuous employment, we merely need to group by those group numbers and find the minimum and maximum date range for the output:
select
employee,
min(`start date`) as `start date`,
max(`end date`) as `end date`
from (
select
e.employee, e.`start date`, e.`end date`
,#curGroup :=
case when employee = #curEmployee
and #curEndDate + INTERVAL 21 DAY >= e.`start date`
then #curGroup
else #curGroup + 1
end as curGroup
,#curEmployee := employee as curEmployee
,#curEndDate := e.`end date` as curEndDate
from
employment e
JOIN (SELECT #curEmployee := 0, #curEndDate := NULL, #curGroup := 0) r
order by e.employee, e.`start date`
) as T
group by curGroup
Sample Result (DEMO):
| EMPLOYEE | START DATE | END DATE |
--------------------------------------------------------------------------------
| 1 | October, 01 2012 00:00:00+0000 | January, 25 2013 00:00:00+0000 |
| 2 | October, 01 2012 00:00:00+0000 | November, 05 2012 00:00:00+0000 |
| 2 | November, 30 2012 00:00:00+0000 | January, 02 2013 00:00:00+0000 |

SSRS: Pivoting columns

I've got a list of raw data which is passed to SSRS from a stored procedure. I have a matrix which then pivots the data.
For example:
Raw data
WeekNumber Date
1 Mon 10th Dec
1 Tue 11th Dec
1 Wed 12th Dec
2 Mon 17th Dec
When pivoted, it becomes the following for the column names
Mon 10th Dec | Tue 11th Dec | Wed 12th Dec | Mon 17th Dec
Is it possible to have a pivot with a where condition? In this example say,
I'd want it to look like
Mon 10th Dec | Tue 11th Dec | Wed 12th Dec
and then another column with Mon 17th Dec since the WeekNumber is 2
I'm not sure I understand your question. But anyway, perhaps you could consider doing the pivot in your stored procedure as per :
http://msdn.microsoft.com/en-us/library/ms177410(v=sql.105).aspx
My secret to success when using reporting tools is to solve complex problems at the data level, rather than trying to get the reporting tool to do it.
Yes, this is not difficult.
What you are calling a pivot in SSRS is really just a column group. You can add either a filter or a parent group to your column group to filter out WeekNumber <> 2 or group above by WeekNumber. With a parent group you could get results like:
WeekNum: 1 | Total for week | |WeekNum: 2 | Total for week |
Mon 10th Dec | Tue 11th Dec | Wed 12th Dec | | |Mon 17th Dec
20 | 25 | 10 | 55 | | 15 | 15