How to print between multiple dates in SQL? - mysql

i'm trying to run mysql query for average vacation hours and sick leaves for the employees that were born
• Between the years 1960 and 1969
• Between the years 1970 and 1979
• Between the years 1980 and 1989
SELECT AVG(VacationHours), AVG(SickLeaveHours)
FROM HumanResources.employee
WHERE BirthDate BETWEEN '1960-1-1' AND '1969-12-31'
OR BirthDate BETWEEN '1970-1-1' AND '1979-12-31'
OR BirthDate BETWEEN '1980-1-1' AND '1980-12-31'
Output
44 49
I want output for individual decade.
something like this.
Output
44 49
44 43
42 47

You can simplify like this when the years are continuous
SELECT Extract(YEAR FROM BirthDate) ,
Avg(VacationHours),
Avg(SickLeaveHours)
FROM HumanResources.employee
WHERE BirthDate BETWEEN '1960-1-1' AND '1980-12-31'
GROUP BY Extract(YEAR FROM BirthDate)

Use GROUP BY with CASE:
SELECT (CASE WHEN BirthDate BETWEEN '1960-01-01' AND '1969-12-31' THEN '1960s'
WHEN BirthDate BETWEEN '1970-01-01' AND '1979-12-31' THEN '1970s'
WHEN BirthDate BETWEEN '1980-01-01' AND '1980-12-31' THEN '1980s'
END) as decade,
AVG(VacationHours), AVG(SickLeaveHours)
FROM HumanResources.employee
WHERE BirthDate >= '1960-01-01' AND BirthDate < '1990-01-01'
GROUP BY decade
ORDER BY decade;
MySQL permits you to use a column alias for GROUP BY. Some databases require that you repeat the expression.

Related

Calculate age from year of birth in mysql

I have a column with multiple values including date of births and year of births
ID OptionValue
1 1992
2 1993-05-31
3 2002
4 1976-06-3
Ideally I want the solution to be
ID OptionValue Age
1 1992 31
2 1993-05-31 29
3 2002 21
4 1976-06-3 46
now
Select TIMESTAMPDIFF(YEAR, lp.optionValue , CURDATE())as Age from learner
and
Select DATE_FORMAT(NOW(), '%Y') - DATE_FORMAT(lp.optionValue , '%Y') - (DATE_FORMAT(NOW(), '00-%m-%d') < DATE_FORMAT(lp.optionValue, '00-%m-%d')) as Age from learner
works perfectly well for me when the date format is 'yyyy-mm-dd' but throws an error for when the year is given only. Can I calculate the age from just given year?
Here is a trick:
Select TIMESTAMPDIFF(YEAR, if(length(lp.optionValue) = 4 , concat(lp.optionValue ,'/01/01'),lp.optionValue), CURDATE())as Age from learner

How to display the days when there are no records in MariaDB?

I have the following table called employees:
employee
name
101
John
102
Alexandra
103
Ruth
And the table called records:
employee
assistance
101
2022-02-01
101
2022-02-02
101
2022-02-07
Let's suppose that I want to display the employee number, name and the days of the month in which there were absences between 2022-02-01 and 2022-02-07 (taking into account that days 05 and 06 are weekends). In that case, the result would be the following:
employee
name
absence
101
John
4,5
How do I get that result?
So far I have developed a query where the days of the month in which there are attendances are displayed. Said query is as follows:
SELECT e.employee,
e.name,
r.assistance AS assistance,
OF employees and
JOIN LEFT(SELECT employee, GROUP_CONCAT(DIFFERENT EXTRACT(DAY SINCE assistance)
ORDER BY STATEMENT(DAY FROM assistance)) AS assistance FROM records
WHERE assistance BETWEEN '2022-02-01' AND '2022-02-07' GROUP BY employee) r ON e.employee = employee
WHERE (r.no_employee IS NOT NULL) ORDER BY name ASC
I would like to know how to implement the days in which there were absences and not consider the weekends. I've done several tests but I'm still stuck. I'm working with MariaDB 10.4.11
You use a recursive common table expression (requires mariadb 10.2+ or mysql 8) to get the list of dates in the date range, and join against that:
with recursive date_range as (
select '2021-12-01' dt
union all
select dt + interval 1 day from date_range where dt < '2021-12-07'
)
select employee.employee, group_concat(day(date_range.dt) order by date_range.dt) faults
from date_range
cross join employee
left join records on records.employee=employee.employee and records.assistance=date_range.dt
where weekday(date_range.dt) < 5 and records.employee is null
group by employee.employee
fiddle
If you are just looking for one employee, add that as a where condition.

How to name rows in SQL

I've got the following query:
(SELECT count(ID)/2 FROM people where Date between '2000' and '2001')
union
(SELECT count(ID)/3 FROM people where Date between '2002' and '2004')
union
(SELECT count(ID)/6 FROM people where Date between '2005' and '2010')
The numbers are correct. Now I want to give names to the rows to see which people are meant.
I only know how to give names to columns thus far.
EDIT: Plus how would I name the column where the new names are in?
Just add one column to the subqueries:
select dt_2000_2001 as dt_range, count(id) cnt from people where date between 2000 and 2001
union all
select dt_2002_2004, count(id) / 2 from people where date between 2002 and 2004
union all
select dt_2005_2010, count(id) / 5 from people where date between 2005 and 2010
I am not convinced that you really need union here. If there are rows for every year, you could use a case expression to build the groups, and count(distinct) to compute the denumerators like so:
select
case when date between 2000 and 2001 then dt_2000_2001
when date between 2002 and 2004 then dt_2002_2004
when date between 2005 and 2010 then dt_2005_2010
end as dt_range,
count(*) / count(distinct date) cnt
from people
group by dt_range
I would suggest:
select (case when date between 2000 and 2001 then '2000-2001'
when date between 2002 and 2004 then '2002-2004'
when date between 2005 and 2010 then '2005-2010'
end) as dt_range,
(count(*) * 1.0 /
(case when date between 2000 and 2001 then 1
when date between 2002 and 2004 then 2
when date between 2005 and 2010 then 5
end)
) as value
from people
group by dt_range;
I'm a bit surprised by your choice of denominator. It is one less than the rate. I would expect the value to be the complete range.
Assuming you have data for all years, you could express this as:
select (case when date between 2000 and 2001 then '2000-2001'
when date between 2002 and 2004 then '2002-2004'
when date between 2005 and 2010 then '2005-2010'
end) as dt_range,
(count(*) * 1.0 /
(max(date) - min(date) + 1)
) as value
from people
group by dt_range;

SQL: Take the oldest date from a table like [ date_year, date_month, date_day ]

i have this table called RELEASE:
*Country, Album, Date_year, Date_month, Date_day*
Italy Z 1940 2 27
Italy Y 1992 11 22
Italy X 1940 1 20
Italy R 1998 null null
France W 1944 9 18
UK L 1989 8 21
UK P 1970 10 1
Germany E 2002 null null
I need to specify a SQL query that take the name of album, the name of country and the date (year, month, day) of the oldest album.
(it's ok also if the values of month and day are null)
I can't use LIMIT, OFFSET, ROWNUM... i can use only standard SQL constructs.
I try to make this query but it isn't correct:
SELECT country, album, min(date_year), min(date_month), min(date_day)
FROM release
The result it would be:
*Country, Album, Date_year, Date_month, Date_day*
Italy X 1940 1 20
How i can solve? Thanks
This should work, ultimately all you need to do is build the date in a sortable format, then sort by it.
select release.country, Album, Date_Year, Date_Month, Date_Day from RELEASE
left join
(
select country,
min(date_year*10000+date_month*100+date_day) minDay
from RELEASE
group by country) albumDay
on albumDay.country = RELEASE.country
where
date_year*10000+date_month*100+date_day = minDay
With the proviso that if you have multiple 'oldest' albums, it will show all of the joint oldest. The problem statement didn't specify how to handle this.
You need to add NULL handling (replace every reference to a date field with coalesce(date_foo,0); or coalesce(date_foo,99); depending on how you want to treat them.
Not tested this, I'd be amazed if it works. Instead of right you should probably use mid(8,len()) h (as the left is always 8 characters)
SELECT
release.country,
right(
min(
cast(Date_year as nvarchar)+ cast(Date_month as nvarchar) + cast(Date_Day as varchar) +album
),1) Album,
min(cast(Date_year as nvarchar)+ cast(Date_month as nvarchar) + cast(Date_Day as varchar) +album) minDate
from release
group by country

Get personal and total worked hours in the same query

I have the following table (simplified):
user_id date hours
1 2012-03-01 5
2 2012-03-01 8
3 2012-03-01 6
1 2012-03-02 3
3 2012-03-02 7
What I want is to get the the sum of hours worked for a given user id (ex. 1), and the total hours worked regardless of what user (for a given time period) in a single query.
So for user_id = 1, and time period: 2012-03-01 - 2012-03-02 the query should return: own=8, total=29.
I can do it in two separate queries, but not in a single one.
Use CASE:
SELECT SUM(
CASE user_id
WHEN 1 THEN hours
ELSE 0
END) as Own,
SUM(hours) as Total
FROM HoursWorked
WHERE date BETWEEN '2012-03-01' AND '2012-03-02';
I think I have something that works using the following schema:
CREATE TABLE hoursWorked
(
id int,
date date,
hours int
);
INSERT INTO hoursWorked
(id, date, hours)
VALUES
('1','2012-03-01','5'),
('2','2012-03-01','8'),
('3','2012-03-01','6'),
('1','2012-03-02','3'),
('3','2012-03-02','7');
And this query:
select parent.id, parent.date, parent.hours, (select sum(hours)
from hoursWorked child
where child.id = parent.id) as totalHours
from hoursWorked parent
I was able to get these results:
ID DATE HOURS TOTALHOURS
1 March, 01 2012 00:00:00-0800 5 8
2 March, 01 2012 00:00:00-0800 8 8
3 March, 01 2012 00:00:00-0800 6 13
1 March, 02 2012 00:00:00-0800 3 8
3 March, 02 2012 00:00:00-0800 7 13
Diego's answer albeit procedural is a great way to get the answer you are looking for. Of course for your date range you would need to add a WHERE date BETWEEN 'startdate' AND 'enddate'. The dates need to be in a format that mysql recognizes, typically 'yyyy-mm-dd'
Another solution that doesn't get you the results in one row, but in a result set would be to do a UNION
SELECT user_id, SUM(hours) as hours FROM table WHERE date BETWEEN 'startdate' AND 'enddate' WHERE user_id = 3
UNION
SELECT null as user_id, SUM(hours) as hours FROM table WHERE date BETWEEN 'startdate' AND 'enddate'