calculate Age from year, month and day fields - mysql

I have a table of people and I need to know how many of them are actual minors.
I have the following query:
SELECT count(*) as minors from
FilesMain a INNER JOIN Sides b
ON a.FileID = b.FileID
INNER JOIN SideData c
ON b.SideDataID = c.SideDataID
WHERE a.StatusCode IN (100,101) AND (YEAR(CURDATE()) - BirthYear<17)
Basically in the query above, I am calculating current date year minus BirthYear field.
I have the persons birth date separated to year, month and day in 3 different fields. please don't ask why. I inherited the data. What would be the correct way to use the Month and Day fields as well to get a more specific result. Just using Year will treats someone born January first and December 31 the same.
Thanks

... AND TIMESTAMPDIFF(YEAR,
CONCAT_WS('-', BirthYear, BirthMonth, BirthDay),
CURRENT_DATE) < 17
Also you may add generated column:
ALTER TABLE tablename
ADD COLUMN DOB DATE
GENERATED ALWAYS AS (CONCAT_WS('-', BirthYear, BirthMonth, BirthDay));
and use this column instead of the above expression.

Related

How to compare a date with array of dates in SQL query and update a field value in MySQL?

I'm working on a task where I need to find the expected date to resolve a ticket using createdAt and sla_name fields values. After that I need to compare the this expected date with the dates in holidays table.
If the expected date falls in holidays, I need to extend the sla_name field value.
This is the query am using.
SELECT t.sla_meet, t.tid, t.ticket_id, t.ticket_name,t.createdAt,t.updatedAt,t.status, dw.dropdown_name
as ticket_priority,p.project_name, dw3.dropdown_name as ticket_status,t.sla as sla_name,
isn.issue_name as issue_type,inn.incidentName as incident_type,t.ticket_accepted_date,
t.asset_id,t.ticket_closed_date,t.contact_number,
IF(NOW() <= DATE_ADD(t.createdAt,INTERVAL (t.sla)+1 DAY),'YES','NO') AS slaMeetData
from tickets t
JOIN assets ast ON t.asset_id=ast.asset_id
JOIN projects p ON p.project_id=ast.project_id
JOIN admin_dropdowns dw ON t.ticket_priority=dw.id
JOIN admin_dropdowns dw3 ON t.ticket_status=dw3.id
JOIN issues isn ON t.issue_type=isn.issue_id
JOIN incident_names inn ON t.incident_type=inn.incidentId
order by t.tid DESC
This is the resultant data of the above query.
Now I need to compare the holidays in above query. And the sample data is,
If the expected date that am getting in IF condition of above query is falls in this holidays, I need to update the sla_name value with COUNT OF HOLIDAYS(If startdata and enddate are there, need to count the days between them) + sla_name.
If expected date is falls on dates range(start and end dates of holidays), need to calculate the count of days from expected date to end date and update that count in sla_name field
Is it possible to do this functionality in SQL? I've used the above query as VIEWS.
Instead of t.sla AS sla_name, use this expression to determine whether to add the length of the overlapping holiday to the number of days:
(
t.sla +
IF(
DATE_ADD(t.createdAt,INTERVAL (t.sla)+1 DAY) BETWEEN holidays.holiday_date AND holidays.end_date,
DATEDIFF( holidays.end_date, holidays.holiday_date ), /* add holiday length number of days */
0 /* no holiday overlap so don't add any days */
)
) as sla_name
You'll also have to join on the holidays table to find the holiday (if any) which overlaps the date in question:
JOIN holidays ON ( DATE_ADD(t.createdAt,INTERVAL (t.sla)+1 DAY) BETWEEN holidays.holiday_date and holidays.end_date )

MySQL query to select all hostels with at least X spaces between start and end dates?

I have 2 tables, one with hostels (effectively a single-room hotel with lots of beds), and the other with bookings.
Hostel table: unique ID, total_spaces
Bookings table: start_date, end_date, num_guests, hostel_ID
I need a (My)SQL query to generate a list of all hostels that have at least num_guests free spaces between start_date and end_date.
Logical breakdown of what I'm trying to achieve:
For each hostel:
Get all bookings that overlap start_date and end_date
For each day between start_date and end_date, sum the total bookings for that day (taking into account num_guests for each booking) and compare with total_spaces, ensuring that there are at least num_guests spaces free on that day (if there aren't on any day then that hostel can be discounted from the results list)
Any suggestions on a query that would do this please? (I can modify the tables if necessary)
I built an example for you here, with more comments, which you can test out:
http://sqlfiddle.com/#!9/10219/9
What's probably tricky for you is to join ranges of overlapping dates. The way I would approach this problem is with a DATES table. It's kind of like a tally table, but for dates. If you join to the DATES table, you basically break down all the booking ranges into bookings for individual dates, and then you can filter and sum them all back up to the particular date range you care about. Helpful code for populating a DATES table can be found here: Get a list of dates between two dates and that's what I used in my example.
Other than that, the query basically follows the logical steps you've already outlined.
Ok, if you are using mysql 8.0.2 and above, then you can use window functions. In such case you can use the solution bellow. This solution does not need to compute the number of quests for each day in the query interval, but only focuses on days when there is some change in the number of hostel guests. Therefore, there is no helping table with dates.
with query as
(
select * from bookings where end_date > '2017-01-02' and start_date < '2017-01-05'
)
select hostel.*, bookingsSum.intervalMax
from hostel
join
(
select tmax.id, max(tmax.intervalCount) intervalMax
from
(
select hostel.id, t.dat, sum(coalesce(sum(t.gn),0)) over (partition by t.id order by t.dat) intervalCount
from hostel
left join
(
select id, start_date dat, guest_num as gn from query
union all
select id, end_date dat, -1 * guest_num as gn from query
) t on hostel.id = t.id
group by hostel.id, t.dat
) tmax
group by tmax.id
) bookingsSum on hostel.id = bookingsSum.id and hostel.total_spaces >= bookingsSum.intervalMax + <num_of_people_you_want_accomodate>
demo
It uses a simple trick, where each start_date represents +guest_num to the overall number of quests and each 'end_date' represents -guest_num to the overall number of quests. We than do the necessary sumarizations in order to find peak number of quests (intervalMax) in the query interval.
You change '2017-01-05' in my query to '2017-01-06' (then only two hostels are in the result) and if you use '2017-01-07' then just hostel id 3 is in the result, since it does not have any bookings yet.

Need help for join and some calculations on a MySql insert

I'll try to provide some context so you can understand what I'm trying to achieve here. My company uses open source software to manage the employees leaves (Jorani, feel free to google it :) ).
There are different types of leave (holidays, sick leave, etc.) and we want to calculate the days "not used" from the holidays of 2016 and "copy" them to another type of leave called "Remaining Holidays 2016".
The important tables are:
entitleddays (here you specify how many days of each type you give to an employee)
id employee startdate enddate type days description
661 3 2016-01-01 2017-02-28 1 14.00 Holidays 2016
1296 3 2016-01-01 2016-12-31 4 18.00 Sick leave 2016
leaves (this table has information about the leaves taken by the employees)
id startdate enddate status employee cause duration type
2436 2016-08-01 2016-08-01 3 78 OK from managers 1.00 1
2766 2016-09-05 2016-09-12 3 63 Holidays 6.00 1
So basically we have:
Entitled leaves:
Data stored in the entitleddays table shown above. In our example let's say I have 14 days for my 2016 holidays.
Taken leaves:
Leaves taken by the user, stored in the table called leaves shown above. For our example let's say I took a day off the first of August and 6 days on September.
Available leaves:
Available days are calculated: entitled days minus "taken leaves". For this examplee, 14 entitled days - 7 = 7 days. So I still have seven days available for holidays :D
So my goal is to insert these 7 days for this user as entitled days for the new type: "Remaining days from 2016" and do this for every user. So the solution that comes up to my mind is to do something like this for every user:
INSERT INTO entitleddays (employee, startdate, enddate, type, days, description)
SELECT id, '2017-01-01', '2017-02-31', '8', (entitled holidays for 2016 minus all the taken leaves of this type), 'Remaining holidays from 2016'
FROM users
Where 8 is the new type of leave where I want to copy the days (Remaining holidays from 2016).
For example I can get the taken holidays from 2016 for a specific user doing this:
SELECT SUM(duration)
FROM leaves
WHERE employee=3 AND status=3 AND type=1
Note: Type 1 is the type of leave "Holidays 2016" and status 3 means that the leave request was accepted.
I can probably achieve all of this in a single SQL instruction but it can also be split in more if simpler or easiest to manage/understand.
Many thanks in advance.
This is how you can handle the calculation:
sum the entitleddays in a subquery by grouping the datasets in its table per employee
maybe even group by year? In this case I just filtered for 2016 via WHERE-clause
sum the taken holidays in a subquery, again by grouping per employee
group by year or filter directly for the one you need
join this subquery onto the other resultset of the other query
calculate (entitled days - taken leaves) in the outer query
Query:
SELECT
entitled.employee,
'2017-01-01',
'2017-02-31',
'8' AS type,
entitled.days - takenDays.days,
'Remaining holidays from 2016'
FROM
(
SELECT
employee,
SUM(days) AS days
FROM
entitleddays
WHERE
startdate >= '2016-01-01'
AND type = 1
GROUP BY
employee
) AS entitled
LEFT JOIN (
SELECT
employee,
SUM(duration) AS days
FROM
`leaves`
WHERE
startdate >= '2016-01-01'
AND type = 1
GROUP BY
employee
) AS takenDays ON takenDays.employee = entitled.employee
I am not sure if this is how you want to calculate the sums for the days of entitleddays and taken days. The query just checks if startdate >= '2016-01-01'.
Also you mentioned a table users in your attempt but didn't provide details for the table, so I left it out. I guess you could use it as a basis otherwise. In the current query the grouped result of entitleddays is the basis.
For the insert
INSERT INTO entitleddays (employee, startdate, enddate, type, days, description)
SELECT
entitled.employee,
'2017-01-01',
'2017-02-31',
'8' AS type,
entitled.days - takenDays.days,
'Remaining holidays from 2016'
FROM
(
SELECT
employee,
SUM(days) AS days
FROM
entitleddays
WHERE
startdate >= '2016-01-01'
AND type = 1
GROUP BY
employee
) AS entitled
LEFT JOIN (
SELECT
employee,
SUM(duration) AS days
FROM
`leaves`
WHERE
startdate >= '2016-01-01'
AND type = 1
GROUP BY
employee
) AS takenDays ON takenDays.employee = entitled.employee

Select leave data from attendance table given the following condition

I have attendance data for employees stored in the table attendance with the following column names:
emp_id (employee ID)
date
type (leave, absent, etc.)
(there are others but I'm omitting them for the sake of simplicity)
My objective is to retrieve all dates of the given month on which the employee was on leave (type = 'Leave') and the last leave taken in the last month, if any.
It's easy to do it using two queries (I'm using PHP to get process the data), but is there any way this can be done in a single query?
I'm answering my own question so as to close it. As #bpgergo pointed out in the comments, UNION will do the trick here.
SELECT * FROM table_name
WHERE type="Leave" AND
date <= (CURRENT_DATE() - 30)
Select the fields, etc you want then se a combined where clause using mysql's CURRENT_DATE() function. I subtracted 30 for 30 days in a month.
If date is a date column, this will return everyone who left 1 month or longer ago.
Edit:
If you want a specific date, change the 2nd month like this:
date <= (date_number - 30)

Get the last twelve months worth of data from a database

I'm trying to work with a database of unemployment figures from the department of labor statistics' data (available at ftp://ftp.bls.gov/pub/time.series/la/)
I need to get the last 12 months of data for any given state, which is trickier then just selecting all data from the last year as they don't always have the last few months of data in yet (right now, the last month's worth of data is November 2010).
I know which record is the newest, and the date fields I have in the database to work with are:
period_name (month name)
year
period (M01, M02, etc for January, February)
My current SQL, which pulls data from a bunch of JOINed tables, is:
USE unemploymentdata;
SELECT DISTINCT series.series_id, period_name, year, value, series.area_code,
footnote_codes, period_name, measure_text, area_text, area_type_text
FROM state_overview
LEFT JOIN series ON state_overview.series_id=series.series_id
LEFT JOIN footnote ON state_overview.footnote_codes = footnote.footnote_code
LEFT JOIN period ON state_overview.period = period.period
LEFT JOIN measure ON series.measure_code = measure.measure_code
LEFT JOIN area ON series.area_code=area.area_code
LEFT JOIN area_type ON area.area_type_code=area_type.area_type_code
WHERE area_text = 'State Name' AND year > 2009
ORDER BY state_overview.period, measure_text;
Any idea?
Since you have textual values to work with for month and year, you'll need to convert them to MySQL-formatted DATE values and can then let MySQL calculate the last year interval like so:
SELECT ... WHERE STR_TO_DATE(CONCAT(period_name,' 1 ',year),'%M %d %Y') >= DATE_SUB(STR_TO_DATE(CONCAT(most_recent_period_name,' 1 ',most_recent_year),'%M %d %Y'), INTERVAL 1 YEAR) ...;
The CONCAT() function is just building a string like "Month 1 YYYY", and the STR_TO_DATE() function is taking that string and a formatting string to tell it how to parse it, and converting it into a DATE.
Note: This query probably sucks index-wise but it should work. : )
I think a few changes to WHERE clause should do it, but for effeciency/simplcity you should also add MAX(year) to the SELECT section.
SELECT ...... MAX(year) as max_year .....
WHERE area_text = 'State Name'
AND year >= max_year - 1
AND period >= (SELECT MAX(period) WHERE year = max_year)
ORDER BY state_overview.period, measure_text;
You can store the year and month as a date, even though you don't have the day information. Just use the first of each month.
{2009, 'M1'} => 2009-01-01
{2009, 'M2'} => 2009-02-01
{2009, 'M3'} => 2009-03-01
This makes date arithmetic much easier than dealing with substrings of (potentially dirty) data. Plus (and this is big), you can index the data much more effective. As a bonus, you can now extract a lot of extra goodies using DATE_FORMAT such as month names, nr of days in month etc.
Does all states have data for all months, and is the data updated at the same time? The answer to that question dictates what query strategy you should use.
The best way is to take the strtotime ($a) of correct 1 year ago and then, when fetching the value from database then find the strtotime ($b) of the date in each result. Now
if($b < $a){
continue;
}
else {
//do something.
}