Get data grouped by 12 weeks period - mysql

I have a table with orders data:
CREATE TABLE `orders` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL DEFAULT '0',
`price` decimal(6,3) unsigned NOT NULL,
`created` datetime DEFAULT NULL,
`paid` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
I need to get price sum of orders in 12 weeks periods, where first period starts on a week when the very first order was paid, and each next period starts one week later than previous (this to have possibility to compare data in 12 weeks periods). So eventually there must be periods like 1-12, 2-13, etc.
Important detail: I can't use variables in query, because this query will be used in BI Tool, where queries with variables behave unexpected.

Did it next way:
SELECT `periods`.*, SUM(`orders`.`price`) AS `revenue`
FROM (SELECT DISTINCT FROM_DAYS(TO_DAYS(`paid`) - MOD(TO_DAYS(`paid`) -1, 7)) AS `period_start`,
(FROM_DAYS(TO_DAYS(`paid`) - MOD(TO_DAYS(`paid`) -1, 7)) + INTERVAL 12 WEEK) AS `period_end`
FROM `orders`
) AS `periods`
LEFT JOIN `orders` ON DATE(`orders`.`paid`) BETWEEN `periods`.`period_start` AND `periods`.`period_end`
GROUP BY `periods`.`period_end`
Small explanation: at first I define 12 week periods in subquery, using same table data. By doing this
DISTINCT FROM_DAYS(TO_DAYS(`paid`) - MOD(TO_DAYS(`paid`) -1, 7))
I shift paid value to the beginning of week.
With this
(FROM_DAYS(TO_DAYS(`paid`) - MOD(TO_DAYS(`paid`) -1, 7)) + INTERVAL 12 WEEK)
I add 12 weeks to the date means beginning of week.
Eventually I get result like this:
| period_start | period_end |
| 2015-07-19 | 2015-10-11 |
| 2015-07-26 | 2015-10-18 |
Then I simply join orders table and group data by end of period.
Looks like it does what I need.

Related

"Row Before" in a sorted query

I'm trying to find a row immediately before and after a given row, with a order by clause. The use case is "previous entry" and "next entry" links in a vaguely blog-like system. The engine is MySQL 5.6. The table schema is
CREATE TABLE `weekly_notes` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`year` int(4) DEFAULT NULL,
`week_number` int(11) DEFAULT NULL,
`header_text` text NOT NULL,
`image_filename` varchar(128) DEFAULT NULL,
`boundry_image_filename` varchar(128) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
and the query is ordered by year desc, week_number desc. The criteria for selecting a row is year and week_number.
Sample Data
insert into weekly_notes values
(101,2018,53,'Week 53 from year 2018',NULL,NULL),
(102,2019,50,'Week 50 from year 2019', NULL, NULL),
(103,2019,51,'Week 51 from year 2019', NULL, NULL),
(104,2019,52,'Week 52 from year 2019', NULL, NULL),
(105,2020,1,'Week 1 from year 2020', NULL, NULL),
(106,2019,53,'Week 53 from year 2019', NULL, NULL),
(107,2020,2,'Week 2 from year 2020', NULL, NULL),
(108,2020,3,'Week 3 from year 2020', NULL, NULL),
(109,2020,4,'Week 4 from year 2020', NULL, NULL);
The select criteria are year an week, so I would like to be able to select the week "before" 2020-01 and get the row for 2019-53 or the week before 2020-03 and get the row for 2020-02
You can use lag() -- on however you are defining the ordering. For instance, if you wanted the row after a certain header_text:
select wn.*
from (select wn.*,
lag(header_text) over (order by year, week) as prev_header_text
from weekly_notes wn
) wn
where prev_header_text = <what you are looking for>
This assumes that "after" is chronological. I suppose "after" with a descending sort could actually mean the row before, in which case you would use lead() instead of lag().
For before a particular week/year combo, you can use:
select wn.*
from (select wn.*,
lag(week) over (order by year, week) as prev_week,
lag(year) over (order by year, week) as prev_year
from weekly_notes wn
) wn
where prev_week = #week and prev_year = #year

Difficulty using LAG to get monthly progress percentage

I have the following table below and would like to take the monthly evolution (%) of total transactions per month. I researched the LAG function but could not understand very well.
I need the return of this query to be like this (Desired Output):
MONTH | TOTAL TRANSACTIONS | % EVOLUTION
----------------------------------------
09 | 45.561 | 0%
10 | 48.598 | 6.66%
UPDATE
% EVOLUTION = ((current value - previous value) / previous value) *
100
It is the formula I use to calculate the evolution of the number of transactions from one month to the previous month.
That is, a column with the previous month's total is required.
DDL
CREATE TABLE IF NOT EXISTS `campanha` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ano_mes` date DEFAULT NULL,
`nome` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
`cpf` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`conta` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`valor` float(10,2) UNSIGNED ZEROFILL NOT NULL,
`transacoes` int(255) DEFAULT NULL,
PRIMARY KEY (`id`)
)
can anybody help me?
MySQL Version: 5.7.23 - MySQL Community Server
http://sqlfiddle.com/#!9/73f38f/2
MySQL 5.x does not support window functions such as LAG. You'll have to do it the old way. Based on table structure the following query should get you started:
SELECT yyyy
, mm
, txn_this_month
, (txn_this_month - (
SELECT SUM(transacoes)
FROM campanha
WHERE ano_mes >= STR_TO_DATE(CONCAT_WS('-', yyyy, mm, 1),'%Y-%c-%e') - INTERVAL 1 MONTH
AND ano_mes < STR_TO_DATE(CONCAT_WS('-', yyyy, mm, 1),'%Y-%c-%e')
)) / txn_this_month * 100 AS perc_change
FROM (
SELECT EXTRACT(YEAR FROM ano_mes) AS yyyy
, EXTRACT(MONTH FROM ano_mes) AS mm
, SUM(transacoes) AS txn_this_month
FROM campanha
GROUP BY EXTRACT(YEAR FROM ano_mes), EXTRACT(MONTH FROM ano_mes)
) AS x
ORDER BY yyyy, mm
The subquery inside select calculates sum for previous month for each row.

SQL update in specific day of week

Hello I have this table
TABLE `record` (
`zpravaID` int(11) NOT NULL AUTO_INCREMENT,
`sensorID` int(11) NOT NULL,
`date_time` datetime NOT NULL,
`cena` double DEFAULT NULL,
`is_cheap` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`zpravaID`), ...)
I would like to do an update which will for example take all records from 2016 and every record, which will be on Monday from 14:00:00 to 14:45:00 will have set the is_cheap to true (1).
I really have no idea, how to do that. Any ideas?
To extract the day of the week you can either use WEEKDAY() or DAYOFWEEK() , the difference between them is that in DAYOFWEEK() sunday is 1 . You can use DATE_FORMAT to extract only the time value, and YEAR() to extract the year of the date :
UPDATE `record` t
SET t.is_cheap = 1
WHERE DAYOFWEEK(t.date_time) = 2
AND DATE_FORMAT(t.date_time,'%H:%i:%s') BETWEEN '14:00:00' AND '14:45:00'
AND YEAR(t.date_time) = 2016
Try this:
update record
set is_cheap = 1
where YEAR(date_time) = 2016
and DAYNAME(date_time) = 'Monday'
and DATE_FORMAT(date_time,'%H:%i:%s') BETWEEN '14:00:00' AND '14:45:00'

mysql to check room availability in a hotel for a particular date and time duration

I need help to find out a query to check the availability of rooms in a hotel for a particular date and time duration.
I am explaining it below.
Table "hotel_room_book" has the folloing columns.
CREATE TABLE IF NOT EXISTS `hotel_room_book` (
`hotel_room_book_id` int(10) NOT NULL AUTO_INCREMENT,
`hotel_room_id` int(10) NOT NULL,
`hotel_id` int(10) NOT NULL,
`who_has_booked` int(10) NOT NULL,
`booked_for_whom` int(10) NOT NULL,
`room_price` float(10,2) NOT NULL,
`book_status` enum('0','1') NOT NULL DEFAULT '0',
`booking_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`checkin_date` date NOT NULL,
`checkin_time` int(4) NOT NULL,
`checkout_date` date NOT NULL,
`checkout_time` int(4) NOT NULL,
PRIMARY KEY (`hotel_room_book_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=20 ;
INSERT INTO hotel_room_book
(hotel_room_book_id,
hotel_room_id, hotel_id,
who_has_booked, booked_for_whom,
room_price, book_status,
booking_date, checkin_date,
checkin_time, checkout_date,
checkout_time) VALUES (1, 13, 1, 1,
1, 564564.00, '0', '2011-06-15
00:00:00', '2011-06-15', 3,
'2011-06-17', 12), (2, 13, 1, 1, 1,
564564.00, '0', '2011-06-15 00:00:00', '2011-06-17', 16, '2011-06-18', 3),
(3, 13, 1, 1, 1, 23.00, '0',
'2011-06-01 00:00:00', '2011-06-19',
5, '2011-06-20', 18);
Means room_id 13 is booked for
slno checkin_date checkin_time checkout_date checkout_time
1 15-06-2011 3 17-06-2011 12
2 17-06-2011 16 18-06-2011 3
3 19-06-2011 5 20-06-2011 18
I am searching for
slno checkin_date checkin_time checkout_date checkout_time
1 17-06-2011 13 17-06-2011 15
2 18-06-2011 4 19-06-2011 4
3 14-06-2011 2 15-06-2011 1
4 20-06-2011 19 21-06-2011 2
I used the following logic for "room available" cases:
case1:(1,2,3 in above)
Room is available
if required checkin_date and checkin_time >= booked checkout_date and checkout_time
and required checkout_date and checkout_time <= booked checkin_date and checkint_time
case2:(4 in above required date matches for availability)
Room is available
if required checkin_date and checkin_time <= booked checkin_date and checkin_time
and required checkout_date and checkout_time <= booked checkin_date and checkint_time
My query is correct if I check manually ,but I get zero record which is obvious.
Hence can you please think on it and help me to find out the query which gives binary result yes/no or 1/0 for available/unavaible of that particular room during that particular period?
In Summary:
Lets say room no 13 is booked from Dt-15-06-2011 at 3 to Dt-17-06-2011 at 12 .
Again it is booked from Dt-17-06-2011 at 16 to Dt-18-06-2011 at 3 .
That means room number 13 is available for the duration Dt-17-06-2011 at 13 to Dt-17-06-2011 at 15 .Now my question is avalability of room 13 is very clear from manual check.But what it is the mysql for it to check programaticaly.
SELECT * FROM hotel_room_book a , hotel_room_book b WHERE a.checkout_date<=
$new_checkin_date AND b.checkin_date >= $new_checkout_date AND a.hotel_id =
b.hotel_id AND a.hotel_room_id = b.hotel_room_id ;
And this will work only if you have checkin_date and checkout_date as TIMESTAMP instead of date, as checking date and time separately will involve complex computation. (Hope you understand the problem in storing date and time in separate columns)
This query is Exactly Correct. ( Booking Date Column range) Just it running inverse .
SELECT * FROM `bookings` WHERE `room_id` = 1
AND booking_end >= $from AND booking_start <= $to

MySQL Get rows between months

I'm trying to SELECT the visitors of my site per month for the current year.
For every different IP/user_agent combination there will be added a row per minute. To track the hits and the unique visitors.
My scheme looks like this:
CREATE TABLE `stats` (
`id` int(11) unsigned NOT NULL auto_increment,
`domain` varchar(40) NOT NULL,
`ip` varchar(20) NOT NULL,
`user_agent` varchar(255) NOT NULL,
`domain_id` int(11) NOT NULL,
`date` timestamp NOT NULL default CURRENT_TIMESTAMP,
`referrer` varchar(400) default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
Now i would like to get all the unique visitors for all the months of a given year.
But i would like to make the unique visitors only unique for 24 hours. So not for the whole month.
It would be possible to just use date >= NOW() - INTERVAL 1 MONTH), but this will jump to 2008 Dec after 2009 Jan. So it should only show the months of a given year.
Are there function to do the same for a month (count the visitors per week, so 4 rows with the first until the fourth week)?
Thanks!
You want to get the number of unique visitors for each site per month?
Something like this should work:
SELECT COUNT(*), domain_id, m, y FROM
(
SELECT ip, user_agent, COUNT(ID) AS hits, domain_id,
DAY(date) as d, MONTH(date) as m, YEAR(date) as y
FROM `stats`
GROUP BY domain_id, ip, d, m, y
) AS tb
GROUP BY tb.m, tb.y
First it groups by day in the subquery, then it groups again by month in the surrounding query.
I'm not entirely sure what your intention was, but you should be able to adapt this technique for your purpose.
(edit: added year component to query)