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...
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 am trying to find the last entry for the previous years quarter.
All I can access is year i.e 2021 and quarter i.e 1
Here is the data in my database:
id
name
start
end
16
April 2021
2021-04-01
2021-04-30
15
March 2021
2021-03-01
2021-03-31
14
February 2021
2021-02-01
2021-02-28
57
November 2020
2020-11-01
2020-11-30
55
October 2020
2020-10-01
2020-10-31
29
September 2020
2020-09-01
2020-09-30
27
July 2020
2020-07-01
2020-07-31
24
April 2020
2020-04-01
2020-04-30
23
March 2020
2020-03-01
2020-03-31
22
February 2020
2020-02-01
2020-02-29
21
January 2020
2020-01-01
2020-01-31
Using the MySQL quarter function I can get it to print out the quarter as an integer in another column:
SET #given_year = 2021;
SET #given_quarter = 1;
SELECT
id, name, start, end, QUARTER(end) as "Q"
FROM
submissions
id
name
start
end
Q
16
April 2021
2021-04-01
2021-04-30
2
15
March 2021
2021-03-01
2021-03-31
1
14
February 2021
2021-02-01
2021-02-28
1
57
November 2020
2020-11-01
2020-11-30
4
55
October 2020
2020-10-01
2020-10-31
4
29
September 2020
2020-09-01
2020-09-30
3
27
July 2020
2020-07-01
2020-07-31
3
24
April 2020
2020-04-01
2020-04-30
2
23
March 2020
2020-03-01
2020-03-31
1
22
February 2020
2020-02-01
2020-02-29
1
21
January 2020
2020-01-01
2020-01-31
1
I tried using WHERE and LIKE but it is returning 0 rows:
SELECT * FROM (
SELECT
id, name, start, end, QUARTER(end) as "Q"
FROM
submissions as s
) AS vs
WHERE
vs.end
LIKE
#given_year
AND
vs.Q < #given_quarter
I also need to account for the possibility that there may be no rows this year and I need to find the previous year.
For example with these two rows, if I was passed the year 2021 and quarter 1 I would need to return November of the previous year and a different quarter.
id
name
start
end
Q
14
February
2021
2021-02-01
2021-02-28
57
November
2020
2020-11-01
2020-11-30
If I understand correctly, you want all the rows from the quarter in the data before a given quarter. You can filter and use dense_rank():
select s.*
from (select s.*,
dense_rank() over (order by year(start) desc, quarter(start) desc) as seqnum
from submissions s
where year(start) < #given_year or
(year(start) = #given_year and quarter(start) < #given_quarter)
) s
where seqnum = 1;
The above returns all rows from the previous quarter (which is what I thought you wanted). If you want only one row:
select s.*
from submissions s
where year(start) < #given_year or
(year(start) = #given_year and quarter(start) < #given_quarter)
order by start desc
limit 1;
I am using MYSQL. I have a table containing a start date and an end date for a schedule.
| StartDate | FinishDate |
|Feb 1, 2016 11:50 AM |Feb 2, 2016 3:37 PM |
|Feb 2, 2016 4:29 PM |Feb 3, 2016 8:16 PM |
|Feb 3, 2016 8:17 PM |Feb 3, 2016 8:34 PM |
What I was hoping to do is to get the dates where there are no schedule entries.
The easiest way I thought of is to generate a new table wherein the values in the FinishDate and StartDate will be rearranged in this format (The FinishDate value in the first row, second column will be the value in the first row, first column, while the StartDate value in the second row, first column will be transported to first row, second cloumn, and so on):
Desired Output
Without allocation:
| StartDate | FinishDate |
|Feb 2, 2016 3:37 PM |Feb 2, 2016 4:29 PM |
|Feb 3, 2016 8:16 PM |Feb 3, 2016 8:17 PM |
|Feb 3, 2016 8:34 PM | - |
How can I achieve the desired output? Thank you very much in advance.
MySql doesn't have window functions, so generating this kind of result is always tricky. One possibility is:
SELECT
t1.FinishDate StartDate
, MIN(t2.StartDate) FinishDate
FROM table t1
LEFT JOIN table t2 ON t2.StartDate > t1.FinishDate
GROUP BY 1
This will work well if your date columns are indexed and you have relatively small number of rows to process.
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.
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.