I know this is a little complicated and I could accomplish this using other methods, but please bear with me.
I am trying to join a table filled with dates with a table filled with events so I can show dates which have no events. I wrote a QUERY that handles this fine, but if I have more than one event on a particular date I can't seem to join multiple records. I have tried all variations of joins.
Please see my problem here
SQL Code
SELECT #RowNumber := #RowNumber + 1 AS DayNumber, D.Date, L.LessonID, L.Title
FROM dates D
JOIN (SELECT #RowNumber:= 0) R
LEFT JOIN lessons L ON L.DayNumber = (#RowNumber+1)
WHERE D.Date IN ('2012-01-01','2012-01-03','2012-01-05','2012-01-10')
ORDER BY DayNumber ASC LIMIT 0, 50
Table Schema
CREATE TABLE IF NOT EXISTS `dates` (
`DateID` int(11) NOT NULL AUTO_INCREMENT,
`Date` date NOT NULL,
`TimeStamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`DateID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `dates` (`DateID`, `Date`, `TimeStamp`) VALUES
(1, '2012-01-01', '2013-02-16 17:38:07'),
(2, '2012-01-02', '2013-02-16 17:38:07'),
(3, '2012-01-03', '2013-02-16 17:38:07'),
(4, '2012-01-04', '2013-02-16 17:38:07'),
(5, '2012-01-05', '2013-02-16 17:38:07'),
(6, '2012-01-06', '2013-02-16 17:38:07'),
(7, '2012-01-07', '2013-02-16 17:38:07'),
(8, '2012-01-08', '2013-02-16 17:38:07'),
(9, '2012-01-09', '2013-02-16 17:38:07'),
(10, '2012-01-10', '2013-02-16 17:38:07');
CREATE TABLE IF NOT EXISTS `lessons` (
`LessonID` int(11) NOT NULL AUTO_INCREMENT,
`DayNumber` int(11) NOT NULL DEFAULT '0',
`Title` varchar(1024) NOT NULL,
`TimeStamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`LessonID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `lessons` (`LessonID`, `DayNumber`, `Title`, `TimeStamp`) VALUES
(1, 1, 'asdfasdf', '2012-01-01 18:03:21'),
(2, 1, 'qwerqwer', '2012-01-05 18:03:21'),
(3, 3, '12341234', '2012-01-05 18:03:34');
Results
Right now this returns this:
DAYNUMBER DATE LESSONID TITLE
1 January, 01 2012 00:00:00+0000 1 asdfasdf
2 January, 03 2012 00:00:00+0000 (null) (null)
3 January, 05 2012 00:00:00+0000 3 12341234
4 January, 10 2012 00:00:00+0000 (null) (null)
But I would like it to return this (note the second row on DayNumber 1 with the title "qwerqwer"):
DAYNUMBER DATE LESSONID TITLE
1 January, 01 2012 00:00:00+0000 1 asdfasdf
1 January, 01 2012 00:00:00+0000 1 qwerqwer
2 January, 03 2012 00:00:00+0000 (null) (null)
3 January, 05 2012 00:00:00+0000 3 12341234
4 January, 10 2012 00:00:00+0000 (null) (null)
If I'm understanding your question correctly, you could do something like this:
SELECT T.DayNumber,
T.Date,
L.LessonId,
L.Title
FROM (
SELECT #RowNumber := #RowNumber + 1 AS DayNumber,
D.Date
FROM dates D
JOIN (SELECT #RowNumber:= 0) R
WHERE D.Date IN ('2012-01-01','2012-01-03','2012-01-05','2012-01-10')
) T LEFT JOIN Lessons L ON
T.DayNumber = L.DayNumber
ORDER BY T.DayNumber ASC LIMIT 0, 50
Here is the updated Fiddle.
And here are the results:
DAYNUMBER DATE LESSONID TITLE
1 January, 01 2012 00:00:00+0000 1 asdfasdf
1 January, 01 2012 00:00:00+0000 2 qwerqwer
2 January, 03 2012 00:00:00+0000 (null) (null)
3 January, 05 2012 00:00:00+0000 3 12341234
4 January, 10 2012 00:00:00+0000 (null) (null)
BTW -- In your results above, you have Lesson Id 1 for your 2nd result -- I assume you meant Lesson Id 2 as in the above results.
Related
My objective is to create a query that shows the person hierarchy for each year. This will be displayed on a "path" generated column with delimited ids. For example, path 1\2\3\4\5 means that 5 is a child of 4, 4 is a child of 3, 3 is a child of 2 and 2 is a child of 1. These relationships must match related date ranges. The tricky part is that the ranges may be different between children and parents so the joins are complex.
I have this table that holds person relationships for date ranges:
CREATE TABLE `persons_x_roles` (
`id` int(11) NOT NULL,
`person_id` int(11) DEFAULT NULL,
`role_from` date DEFAULT NULL,
`role_to` date DEFAULT NULL,
`parent_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
)
where parent_id is the FK from a table persons.
The top person in the hierarchy has him/herself as parent (I know it's not perfect design and that it should have parent_id = NULL). Nevertheless, I have the following recursive SQL to get the hierarchies:
WITH RECURSIVE roles_path AS (
SELECT
persons_x_roles.id,
person_id,
role_from,
YEAR ( role_from ) AS effective_year,
role_to,
parent_id,
1 AS depth,
cast(
person_id AS VARCHAR ( 32 )) AS person_path
FROM
persons_x_roles
WHERE
person_id = 1 UNION ALL
SELECT
child_roles.id,
child_roles.person_id,
child_roles.role_from,
YEAR ( child_roles.role_from ) AS effective_year,
child_roles.role_to,
child_roles.parent_id,
parent_roles.depth + 1,
CONCAT( parent_roles.person_path, '\\', child_roles.person_id )
FROM
roles_path AS parent_roles
LEFT JOIN persons_x_roles AS child_roles ON (
parent_roles.person_id = child_roles.parent_id
AND parent_roles.role_from <= child_roles.role_from
AND IFNULL( parent_roles.role_to, CURRENT_DATE ) >= IFNULL( child_roles.role_to, IFNULL( parent_roles.role_to, CURRENT_DATE ) )
)
WHERE
child_roles.person_id != 1
AND child_roles.parent_id IS NOT NULL
) SELECT
*
FROM
roles_path
ORDER BY
person_path
The persons_x_roles table, for person 14073, has these entries:
INSERT INTO `visitsmgmt_jpa`.`persons_x_roles`(`id`, `person_id`, `role_from`, `role_to`, `parent_id`) VALUES (14073, 14073, '2016-01-01', '2016-12-31', NULL);
INSERT INTO `visitsmgmt_jpa`.`persons_x_roles`(`id`, `person_id`, `role_from`, `role_to`, `parent_id`) VALUES (25698, 14073, '2017-01-01', '2017-12-31', 26);
INSERT INTO `visitsmgmt_jpa`.`persons_x_roles`(`id`, `person_id`, `role_from`, `role_to`, `parent_id`) VALUES (45195, 14073, '2018-01-01', '2018-12-31', 26);
INSERT INTO `visitsmgmt_jpa`.`persons_x_roles`(`id`, `person_id`, `role_from`, `role_to`, `parent_id`) VALUES (54710, 14073, '2019-01-01', NULL, 26);
select * from persons_x_roles where person_id=14073
14073 14073 5 2016-01-01 2016-12-31 [NULL]
25698 14073 5 2017-01-01 2017-12-31 26
45195 14073 5 2018-01-01 2018-12-31 26
54710 14073 5 2019-01-01 [NULL] 26
For the parent person (26), it has these:
INSERT INTO `visitsmgmt_jpa`.`persons_x_roles`(`id`, `person_id`, `role_from`, `role_to`, `parent_id`) VALUES (26, 26, '2016-01-01', '2019-12-31', 6);
INSERT INTO `visitsmgmt_jpa`.`persons_x_roles`(`id`, `person_id`, `role_from`, `role_to`, `parent_id`) VALUES (69338, 26, '2020-01-01', NULL, 4);
select * from persons_x_roles where person_id=26
26 26 1 4 2016-01-01 2019-12-31 6
69338 26 1 4 2020-01-01 [NULL] 4
However, if I run the recursive SELECT for the child person, I'm missing some rows:
select * from persons_x_roles_path where person_id=14073
45195 14073 2018-01-01 2018 2018-12-31 26 5 1\2\6\26\14073
25698 14073 2017-01-01 2017 2017-12-31 26 5 1\2\6\26\14073
54710 14073 2019-01-01 2019 [NULL] 26 5 1\2\6\26\14073
The missing row is for 2020. While the parent (26) has one closed-ended record for 2019 and one open-ended record for 2020, the child has an open-ended record starting 2019.
So, I would expect the results to be like this:
45195 14073 2018-01-01 2018 2018-12-31 26 5 1\2\6\26\14073
25698 14073 2017-01-01 2017 2017-12-31 26 5 1\2\6\26\14073
54710 14073 2019-01-01 2019 2019-12-31 26 5 1\2\6\26\14073
69338 14073 2020-01-01 2020 2020-12-31 26 5 1\2\6\26\14073
or
45195 14073 2018-01-01 2018 2018-12-31 26 5 1\2\6\26\14073
25698 14073 2017-01-01 2017 2017-12-31 26 5 1\2\6\26\14073
54710 14073 2019-01-01 2019 2019-12-31 26 5 1\2\6\26\14073
69338 14073 2020-01-01 2020 [NULL] 26 5 1\2\6\26\14073
I want to group and show the number of registered users per week, from a database.
My code is almost perfect, but it doesn't work correct for year 2016, here is the database:
(1, '2013-10-14 16:22:10'),
(2, '2013-12-28 16:22:10'),
(3, '2014-01-03 16:22:10'),
(4, '2014-01-08 16:22:10'),
(5, '2014-06-22 16:22:10'),
(6, '2014-12-19 16:22:10'),
(7, '2014-12-27 16:22:10'),
(8, '2015-01-01 16:22:10'),
(9, '2015-01-07 16:22:10'),
(10, '2015-12-12 16:22:10'),
(11, '2015-12-28 16:22:10'),
(12, '2015-12-29 16:22:10'),
(13, '2016-01-05 16:22:10'),
(14, '2016-01-10 16:22:10'),
(15, '2016-01-15 16:22:10');
And this is the query:
select
str_to_date(concat(yearweek(reg_data), ' monday'), '%X%V %W') as startingweek,
YEAR(reg_data)*52+WEEK(reg_data, 1) - YEAR('2013-10-14')*52 - WEEK('2013-10-14', 1) + 1 as week_number,
count(a.reg_data) as nr
from users as a
group by yearweek(reg_data, 1)
order by yearweek(reg_data, 1);
This is the result, the last 2 rows from 2016 are incorrect, I put in parentheses the correct value that I am expecting:
startingweek week_number nr
October, 14 2013 00:00:00 1 1
December, 23 2013 00:00:00 11 1
December, 30 2013 00:00:00 12 1
January, 06 2014 00:00:00 13 1
June, 23 2014 00:00:00 36 1
December, 15 2014 00:00:00 62 1
December, 22 2014 00:00:00 63 1
December, 29 2014 00:00:00 64 1
January, 05 2015 00:00:00 65 1
December, 07 2015 00:00:00 113 1
December, 28 2015 00:00:00 116 2
January, 04 2016 00:00:00 116 (117) 2
January, 11 2016 00:00:00 117 (118) 1
I know the problem is regarding to the number of weeks I use in my query -> 52, because some years have 53 weeks, but I don't know how to make that dynamically change depending on the year.
Also I made a sqlfiddle here: http://sqlfiddle.com/#!9/1e5df5/2
Simply change the calculation to the number of days between the date and a start date and divide by 7:
select str_to_date(concat(yearweek(reg_data), ' monday'), '%X%V %W') as startingweek,
floor(datediff(min(reg_data),'2013-10-14')/7)+1 as week_number,
count(a.reg_data) as nr
from users as a
group by yearweek(reg_data, 1)
order by yearweek(reg_data, 1);
See fiddle
Many databases implement the datediff() function. So DATEDIFF(week, date1, date2) is what you need. Different databases might use slightly different syntax...
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
I'm new to sql servers, I have a table as follows
**Emp_name | year | rank **
emp1 2010 1
emp1 2011 2
emp1 2012 3
emp2 2012 1
emp2 2013 2
emp3 2009 3
emp2 2010 4
emp3 2011 5
Emp_name column has duplicate values, I want a query that will return following result,
**Emp_name | year | rank **
emp1 2010 1
null (emp1) 2011 2
null (emp1) 2012 3
emp2 2012 1
null (emp2) 2013 2
emp3 2009 3
null (emp2) 2010 4
null (emp3) 2011 5
Only 1 of the values from the duplicate values(Emp_name column should be seen and the rest as null or as a blank space.
try this:
CREATE TABLE Table1
(Emp_name varchar(4), year int, rank int)
;
INSERT INTO Table1
(Emp_name, year, rank)
VALUES
('emp1', 2010, 1),
('emp1', 2011, 2),
('emp1', 2012, 3),
('emp2', 2012, 1),
('emp2', 2013, 2),
('emp3', 2009, 3),
('emp2', 2010, 4),
('emp3', 2011, 5)
;
select
CASE WHEN rn=1 then EMP_NAME else null end EMP_NAME,
year,rank
from
(
select *,ROW_NUMBER() over (partition by emp_name order by (select 0)) as rn from Table1
) a
I have output as below
ID Date
Null 2012-10-01
1 2012-10-02
2 2012-10-03
NULL 2012-10-04
3 2012-10-05
NULL 2012-10-06
4 2012-10-07
NULL 2012-10-08
5 2012-10-10
NULL 2012-10-11
NULL 2012-10-12
6 2012-10-13
NULL 2012-10-16
As it has missing dates with value as NULL. I need to show final output as
2012-10-01 - 2012-10-01 (1 day )
2012-10-04 - 2012-10-04(1 day )
2012-10-06 - 2012-10-06(1 day )
2012-10-08 - 2012-10-08(1 day )
2012-10-11 - 2012-10-12(2 day )
2012-10-14 - 2012-10-14(1 day )
You can generate the date ranges using the following query:
select
min(date) as start,
max(date) as end,
datediff(max(date), min(date)) + 1 as numDays
from
(select #curRow := #curRow + 1 AS row_number, id, date
from Table1 join (SELECT #curRow := 0) r where ID is null) T
group by
datediff(date, '2012-10-01 00:00:00') - row_number;
The logic is based on a clever trick for grouping consecutive ranges. First, we filter and number the rows in the subquery. Then, the rows that are grouped together are found by comparing the number of days after 2012-10-01 to the row number. If any rows share this value, then they must be consecutive, otherwise there would be a "jump" between two rows and the expression datediff(date, '2012-10-01 00:00:00') - row_number would no longer match.
Sample output (DEMO):
START END NUMDAYS
October, 01 2012 00:00:00+0000 October, 01 2012 00:00:00+0000 1
October, 04 2012 00:00:00+0000 October, 04 2012 00:00:00+0000 1
October, 06 2012 00:00:00+0000 October, 06 2012 00:00:00+0000 1
October, 08 2012 00:00:00+0000 October, 08 2012 00:00:00+0000 1
October, 11 2012 00:00:00+0000 October, 12 2012 00:00:00+0000 2
October, 16 2012 00:00:00+0000 October, 16 2012 00:00:00+0000 1
From there I think it should be pretty trivial for you to get the exact output you are looking for.