Get all months with number of months and days from 2 date? - php

Here is my 2 mysql tables. In clients table is showing user connection date and in clients_pay_bill table showing in which month user is paid his/her bill from connection date (conn_date).
a) clients
clients_id conn_date
=======================
1 2016-06-01
2 2016-07-17
3 2016-06-22
4 2016-09-03
b) clients_pay_bill
cpp_id clients_id paid_month
===================================
1 1 2016-07-03
2 2 2016-07-22
3 4 2016-09-09
4 2 2016-07-22
Now I want to show all months with number of days and months of which clients is not paid until current date.
For example :
clients_id = 1 connection date (conn_date) is 2016-06-01 and he only paid 2016-07-03 month bill till now. So the sql query will be output following months:
2016-06
2016-08
2016-09
**2016-07 will not print because he already paid this months bill**
and I also want to show number of days and months e.g: 3 months some days..
I can't imagine how the sql query should look like ?

I will suggest you a bit different approach cause this one will kill you both logically and performance speaking. The way it should be done is to have records genreated in clients_pay_bill not when someone pays but when he/she should pay and as an addition to it there should be 2 columns: due_date and paid_date. So your table would look like this:
cpp_id clients_id due_month paid_month
====================================================
1 1 2016-06-03 NULL
1 1 2016-07-03 2016-07-03
1 1 2016-08-03 NULL
2 2 2016-09-22 NULL
3 4 2016-09-09 NULL
4 2 2016-07-22 NULL
You could generate monthly due note via cron or if you prefer to stay in SQL than via events. After that you have simple query selecting which due note has NULL in paid_month cause if payment was done than you would update paid_month column.
Regarding days I am not sure what effect you would like to achieve (example would help) but having list of due notes it's already half way to make calculations regarding amount of days on these records (eg. with DATEDIFF)

Related

Mysql GROUP BY and Union based on date condition

Here is my query - it mostly works, but I can see it failing on one condition - explained after the query:
$firstDay = '2020-03-01' ;
$lastDay = '2020-03-31' ;
SELECT * FROM clubEventsCal
WHERE ceFreq!=1
AND (ceDate>='$firstDay' AND ceDate<='$lastDay')
UNION SELECT * FROM clubEventsCal
WHERE ceFreq=1
AND (ceDate>='$firstDay' AND ceDate<='$lastDay')
GROUP BY ceStopDate ORDER BY ceID,ceDate ;
The first select gives me all Event records between the two dates. The second select gives me grouped/summarized Event records between the two dates. The problem though is if the value ceDate spans days across two months: IE: 2020-03-30 thru 2020-04-02. When I pull the records for March, all is good - the above query pulls the 2020-03-30 record (grouped) as the first instance of the 4 days/records - allowing us to charge for a single 4 day event. But when I pull the records for April its also going to pull 2020-04-01 as a new grouped Event record for the last two days of the 4 day event and try to charge the customer for a new Event - when in fact those two days were already a part of March's bill.
How can I write the query so that when ceDate starts in Month X but ends in Month Y that when records are pulled for Month Y its not trying to pull records that actually belong to an Event that started in Month X?
Examples of an Event record would look like this:
rid | ceID | ceActive | ceFreq | ceDate | ceStopDate
------------------------------------------------
1 1108 1 3 2020-03-09 | 2020-03-09
2 1111 1 2 2020-03-15 | 2020-03-15
3 1112 1 2 2020-03-17 | 2020-03-17
4 1117 1 1 2020-03-30 | 2020-04-02
5 1117 1 1 2020-03-31 | 2020-04-02
6 1106 1 3 2020-03-21 | 2020-03-21
7 1110 1 2 2020-03-05 | 2020-03-05
8 1113 1 2 2020-03-24 | 2020-03-24
9 1117 1 1 2020-04-01 | 2020-04-02
10 1117 1 1 2020-04-02 | 2020-04-02
The above query pulls all records where ceFreq != 1, and it pulls a single record for the ceFreq = 1 records (rids: 4 & 5). For March, we don't necessarily care that ceID 1117 spills into April. But when we pull records for April - we need to exclude rid 9 & 10, because the Event (ceID=1117), was already accounted for in March.
SELECT * FROM clubEventsCal
...
GROUP BY ceStopDate
This is gibberish.
MySQL (depending on configuration) allows it without choking - but it's semantically wrong and stands out as an anti-pattern.
There are some edge cases where the values returned might contain significant data, but they very unusual. Trying to explain a problem with code which does not work is perhaps not a good strategy.
Looking at your code, its possible that you don't need a union - but there's not enough information in your example records to say if this would actually give the result you expect (it will be significantly faster depending on your indexes):
SELECT IF(cefreq=1, rid, null) AS consolidator
, ceid
, cefreq
, MIN(cedate), MAX(cedate)
, ceStopDate
FROM clubEventsCal
WHERE cID=1001
AND ceActive!=2
AND (ceDate>='$firstDay' AND ceDate<='$lastDay')
GROUP BY IF(cefreq=1, rid, null)
, ceid
, cefreq
, ceStopDate
;
I would have added the ORDER BY - but I don't know where clId came from. Also This will give different resuts to what I think you were trying to achieve for any record where cefreq is null (if you really do want to exclude them, add a predicate in the WHERE clause).

Convert MySQL time stamp into DAY then sort by MAX

Trying to write a mySQL query that selects the most recent(**) number of times a light was turned on by a user.
**Most recent being all the times the light was turned on for the most recent day in the DB.
Sample Table:
DB Name: LLL
Table Name: Lights
UserID | LightOn | LightOff
-----------------------------------------------------
3 | 2018-01-08 09:00:00 | 2018-01-08 09:03:00
3 | 2018-01-08 10:15:00 | 2018-01-08 10:17:00
3 | 2018-01-07 15:00:00 | 2018-01-07 15:05:00
So, From this table, we can tell that
UserID 3 (Bob) turns the light on:
2 times on January 8th (at 9AM for 3 minutes and 10:15AM for 2 minutes) &
1 times on January 7th (at 3PM for 5 mins)
I want my query to return 2, because there are 2 records for the most recent day of January 8th.
I'm at the point where I can only get the number of records:
SELECT COUNT(C.LightOff) AS count FROM LLL.Lights AS C
WHERE C.UserID = 3
ORDER BY C.LightsOff DESC
I get the following back:
count
-------
3
I need to figure out a way to convert the time stamp into a DAY and get all the records that match that MAX Day.
The desired result is:
count
-------
2
Any ideas?
Assuming you have a proper datetime value in you lightoff column
you could get the most recent day and join with your count
select count(*) from LLL.Lights
inner join (
select max(date(LightOff)) max_date
FROM LLL.Lights
WHERE UserID = 3
) t on t.max_date = date(LightOff)

insert Time in to database using Codeiniter

I have a form which is for company registration,Which has fields for open hours and close hours for each day in a week.
I have Two Tables Store the time
Timing | Week
In my timing Table i have four columns
id | company_id | day | open |close
week Table contain each day id
id | name
Suppose on Monday the company which is open on 8 am and closed on 3 pm .
Like Monday user fill all the days like whenever is open and close,
Now how can i insert these data in by its day by day into timing Table.
id c_id day open close
1 1 1 06:28:13 14:47:32
2 1 2 06:28:13 14:47:32
3 1 3 06:28:13 16:47:32
4 1 4 06:28:13 14:00:32
5 1 5 06:28:13 12:07:32
6 1 6 06:28:13 18:47:32
7 1 7 06:28:13 19:47:32
I want to store some thing like this here day column refers to the week table which has id of the name of day.
I do not understand how to insert. i have also tried to insert_batch.
This is my form
Controller:
i have tried many times but i could not find way to do this.
I know this is not the right way, but How can i do this???
$time['open']['monday_open'] = $this->input->post('monday');
$time['close']['monday_close'] = $this->input->post('monday_close');

Creating a table for each individual user's timetable data

I'm coding an online timetable website which holds all your current subjects you are attending at school and contains slots to write work and it's due date. Mainly for my own personal use, but I guess I could share the URL with a few people when it's completed. Anyway, I have a login / register system set up, linked to a MySQL database. I have the following columns in a table called 'users'.
userid, username, firstname, lastname, password
Before I attempt anything too stupid, I wanted somebody to give an opinion on what I am about to attempt...
I thought I could write some PHP that creates a table for each new user when they sign up. It would contain all their subjects for each day of the week, and once they input their data it would write it to the database and they wouldn't have to edit their information unless they had to (subject change, etc...)
Would a whole new table for each user's subject data be efficient? The data would have two dimensions: The day of the week (x axis) and the periods of the school day where the subjects are situated (periods 1-6 for my school)
Anyway, thanks for reading, opinions on the best way to go around doing this would be helpful. Thank you.
EDIT: Strawberry's suggestion
userid,day,period,subjectid
1 1 1 4
1 1 2 2
1 1 3 5
1 1 4 3
1 1 5 1
1 1 6 7
2 1 1 4
2 1 2 2
2 1 3 5
2 1 4 3
2 1 5 1
2 1 6 7

How to find exact seasons and days from a given date range?

I need this for rent a car price calculation. Cars prices are different according to seasons.
I have a season_dates table like this
id slug start end
1 low 2011-01-01 00:00:00 2011-04-30 00:00:00
2 mid 2011-05-01 00:00:00 2011-06-30 00:00:00
3 high 2011-07-01 00:00:00 2011-08-31 00:00:00
4 mid 2011-09-01 00:00:00 2011-10-31 00:00:00
5 low 2011-11-01 00:00:00 2011-12-31 00:00:00
Users selecting days, for example:
start_day 08/20 end_day 08/25
My query like that:
SELECT * from arac_donemler
where DATE_FORMAT(start, '%m/%d') <= '08/20'
and DATE_FORMAT(end, '%m/%d') >= '08/25'
This gives me high season that's correct.
But what I couldn't handle is: what if user selects a date range between 2 seasons?
For example from 20 August to 05 September.
This time I have to find that date ranges belongs to which seasons?
And I have to calculate how many days per each seasons?
For the example above,
high season ending at 31 August. So 31-20 = 11 days for high season, 5 days for mid season.
How can I provide this separation?
I hope I could explain it.
I tried so many things like join table inside but couldn't succeed it.
I'll let others chime in with the right way to do date comparisons in SQL (yours almost certainly kills indexing for the table), but for a start, you can get exactly the seasons that are relevant by
select * from arac_donemler
where end >= [arrival-date]
and start <= [departure-date]
Then you should do the rest of your processing (figure out how many days in each season and so forth) in the business logic instead of in the database query.
I would store all single days within a table.
This is a simple example.
create table dates (
id int not null auto_increment primary key,
pday date,
slug tinyint,
price int);
insert into dates (pday,slug,price)
values
('2011-01-01',1,10),
('2011-01-02',1,10),
('2011-01-03',2,20),
('2011-01-04',2,20),
('2011-01-05',2,20),
('2011-01-06',3,30),
('2011-01-07',3,30),
('2011-01-08',3,30);
select
concat(min(pday),'/',max(pday)) as period,
count(*) as days,
sum(price) as price_per_period
from dates
where pday between '2011-01-02' and '2011-01-07'
group by slug
+-----------------------+------+------------------+
| period | days | price_per_period |
+-----------------------+------+------------------+
| 2011-01-02/2011-01-02 | 1 | 10 |
| 2011-01-03/2011-01-05 | 3 | 60 |
| 2011-01-06/2011-01-07 | 2 | 60 |
+-----------------------+------+------------------+
3 rows in set (0.00 sec)
EDIT. Version with grandtotal
select
case
when slug is null then 'Total' else concat(min(pday),'/',max(pday)) end as period,
count(*) as days,
sum(price) as price_per_period
from dates
where pday between '2011-01-02' and '2011-01-07'
group by slug
with rollup;
+-----------------------+------+------------------+
| period | days | price_per_period |
+-----------------------+------+------------------+
| 2011-01-02/2011-01-02 | 1 | 10 |
| 2011-01-03/2011-01-05 | 3 | 60 |
| 2011-01-06/2011-01-07 | 2 | 60 |
| Total | 6 | 130 |
+-----------------------+------+------------------+
4 rows in set (0.00 sec)
edit. Stored procedure to populate table
delimiter $$
create procedure calendario(in anno int)
begin
declare i,ultimo int;
declare miadata date;
set i = 0;
select dayofyear(concat(anno,'-12-31')) into ultimo;
while i < ultimo do
select concat(anno,'-01-01') + interval i day into miadata;
insert into dates (pday) values (miadata);
set i = i + 1;
end while;
end $$
delimiter ;
call calendario(2011);
If you have a table RENTAL too (the real version would need a lot of other details in it):
CREATE TABLE Rental
(
start DATE NOT NULL,
end DATE NOT NULL
);
and you populate it with:
INSERT INTO rental VALUES('2011-08-20', '2011-09-05');
INSERT INTO rental VALUES('2011-08-20', '2011-08-25');
then this query produces a plausible result:
SELECT r.start AS r_start, r.end AS r_end,
s.start AS s_start, s.end AS s_end,
GREATEST(r.start, s.start) AS p_start,
LEAST(r.end, s.end) AS p_end,
DATEDIFF(LEAST(r.end, s.end), GREATEST(r.start, s.start)) + 1 AS days,
s.id, s.slug
FROM rental AS r
JOIN season_dates AS s ON r.start <= s.end AND r.end >= s.start;
It yields:
r_start r_end s_start s_end p_start p_end days id slug
2011-08-20 2011-09-05 2011-07-01 2011-08-31 2011-08-20 2011-08-31 12 3 high
2011-08-20 2011-09-05 2011-09-01 2011-10-31 2011-09-01 2011-09-05 5 4 mid
2011-08-20 2011-08-25 2011-07-01 2011-08-31 2011-08-20 2011-08-25 6 3 high
Note that I'm counting 12 days instead of 11; that's the +1 in the days expression. It gets tricky; you have to decide whether if the car is returned on the same day as it is rented, is that one day's rental? What if it is returned the next day? Maybe the time matters? But that gets into detailed business rules rather than general principles. Maybe the duration is the larger of the raw DATEDIFF() and 1? Also note that there is only the rental start and end dates in this schema to identify the rental; a real schema would have some sort of Rental Agreement Number in the rental table.
(Confession: simulated using IBM Informix 11.70.FC2 on MacOS X 10.7.1, but MySQL is documented as supporting LEAST, GREATEST, and DATEDIFF and I simulated those in Informix. The most noticeable difference might be that Informix has a DATE type without any time component, so there are no times needed or displayed.)
But [...] seasons period always same every year. So I thought to compare only days and months. 2011 isn't important. Next years just 2011 will be used. This time problem occurs. For example low season includes November, December and then go to January, February, March, April. If a user selects a date range 01.05.2011 to ...2011 There is no problem. I just compare month and day with DATE_FORMAT(end, '%m/%d'). But if he chooses a range from December to next year January, how am I gonna calculate days?
Notice that 5 entries per year in the Season_Dates table is not going to make an 8" floppy disk break sweat over storage capacity for a good few years, let alone a 500 GiB monster disk. So, by far the simplest thing is to define the entries for 2012 in 5 new rows in the Season_Dates table. That also allows you to handle the fact that in December, the powers-that-be decide the rules will be different (20th December to 4th January will be 'mid', not 'low' season, for example).

Categories