I'm trying to create a simple function that will allow me to determine the salary of an employee depending on the year they were hired, and then insert the salary they have into a column called "Salary" on that table.
If the employee was hired in 2011 his salary would be 5,000
If the employee was hired in 2012 his salary would be 6,000
If the employee was hired in 2013 his salary would be 7,000
The date they were hired in is stored in a column called "Hired", I can't figure out how to extract the data and use it as a number to define the salary with an IF statement. I know I probably need a TO_char function too, but I don't know where to use it to get the date converted into a string.
Thanks for any help/ideas on how to do this.
You would use a case statement. Here is one way:
select (case when to_char(DateHired, 'YYYY') = '2011' then 5000
when to_char(DateHired, 'YYYY') = '2012' then 6000
when to_char(DateHired, 'YYYY') = '2013' then 7000
end) as Salary
Related
I have an employees table with their name, hire_date and salary on it. Now what I am trying to get is the each year's spending of the company on salaries, but every time I try to do it, it gives me each hire_date in the output and respective salary.
select
hire_date, dateadd(year, 1, hire_date), sum(salary)
from
employees
where
hire_date between hire_date and dateadd(year, 1, hire_date)
group by
hire_date
The answer is in your requirements. If you need the total salary cost per year it is implied you use SUM on salary and GROUP BY year:
select
YEAR(hire_date), SUM(salary)
from
employees
group by
YEAR(hire_date)
As the title says the query needs to combine multiple select queries. The question is as follows:
Display the total number of employees, and of that total the number of employees hired in 1995,1996,1997,1998.
My query:
select (select count(*) from employees) as "Total",
(select count(*) from employees where hire_date between 'JAN-1-0095' and 'DEC-1-0095')as "1995",
(select count(*) from employees where hire_date between 'JAN-1-0096' and 'DEC-1-0096') as "1996",
(select count(*) from employees where hire_date between 'JAN-1-0097' and 'DEC-1-0097') as "1997",
(select count(*) from employees where hire_date between 'JAN-1-0098' and 'DEC-1-0098') as "1998"
from employees
but the issue is instead of returning only single record this query is being executed for all the records in the table and hence producing the following output:
You can use conditional counting:
select count(*) as total_count,
count(case when extract(year from hire_date) = 1995 then 1 end) as "1995",
count(case when extract(year from hire_date) = 1996 then 1 end) as "1996",
count(case when extract(year from hire_date) = 1997 then 1 end) as "1997",
count(case when extract(year from hire_date) = 1998 then 1 end) as "1997",
from employees;
this makes use of the fact that aggregate functions ignore NULL values and therefor the count() will only count those rows where the case expressions returns a non-null value.
Your query returns one row for each row in the employees table because you do not apply any grouping. Each select is a scalar sub-select that gets executed for each and every row in the employees table.
You could make it only return a single row if you replace the final from employees with from dual - but you'd still count over all rows within each sub-select.
You should also avoid implicit data type conversion like you did. 'JAN-1-0095' is a string and will implicitly be converted to a date depending on your NLS settings. Your query would not run if executed from my computer (because of different NLS settings).
As you are looking for a complete year, just comparing the year is a bit shorter to write and easier to understand (at least in my eyes).
Another option would be to use proper date literals, e.g. where hire_date between DATE '1995-01-01' and DATE '1995-12-31' or a bit more verbose using Oracle's to_date() function: where hire_date between to_date('1995-01-01', 'yyyy-mm-dd') and to_date('1995-12-31', 'yyyy-mm-dd')
Assuming the years are really what you want, the problem with your query is that you are selecting from employees, so you get a row for each one. You could use:
select (select count(*) from employees) as "Total",
(select count(*) from employees where hire_date between 'JAN-1-0095' and 'DEC-1-0095')as "1995",
(select count(*) from employees where hire_date between 'JAN-1-0096' and 'DEC-1-0096') as "1996",
(select count(*) from employees where hire_date between 'JAN-1-0097' and 'DEC-1-0097') as "1997",
(select count(*) from employees where hire_date between 'JAN-1-0098' and 'DEC-1-0098') as "1998"
from dual;
And I would use date '1998-01-01' for the date constants.
However, I prefer #a_horse_with_no_name's solution.
You should avoid using a lot of subqueries. You should try this:
SQL Server:
SELECT count(*) as Total, hire_date
FROM employees
WHERE year(hire_date) IN ('1995','1996','1997','1998')
GROUP BY hire_date WITH CUBE
In ORACLE
SELECT count(*) as Total, hire_date
FROM employees
WHERE extract(year from hire_date) IN ('1995','1996','1997','1998')
GROUP BY CUBE (hire_date)
In addition to the subtotals generated by the GROUP BY, the CUBE extension will generate subtotals for each hire_date.
I'm trying to write a sql query to figure out by how much have salaries increased in the last year for each department due to new employees.
Table structure is Employees (empno, deptno, msal, hiredate)
I can figure out how to get all the salaries by departments
SELECT sum(msal) FROM employees GROUP BY deptno;
and how to get the salaries from people who were hired in the past year
SELECT sum(msal) FROM employees WHERE hiredate > (DATEADD(year, -1, GETDATE())) GROUP BY deptno;
But whatever way I try to subtract the result of these two queries I only get errors.
Here is what you might do. In this case I'm using a CASE statement to filter new employees:
SELECT SUM(msal) - SUM(CASE WHEN hiredate > ADD_MONTHS(SYSDATE, -12) THEN msal ELSE 0 END)
FROM employees
GROUP BY deptno;
FYI, Oracle doesn't have a DATEADD() function nor does it have a GETDATE() function. Note that I used ADD_MONTHS() and SYSDATE (you could also use CURRENT_DATE) in place of these.
Why not just change the direction of the where?
SELECT sum(msal)
FROM employees
WHERE hiredate <= DATEADD(year, -1, GETDATE())
GROUP BY deptno;
Also, normally when you aggregate by a field, then the field (deptno) is included in the select clause.
Can anyone help me , i need to write query for next case .
It is necessary to draw from the table asking the average salary of employees in the company for more than 5 years, and the average salary of employees in the company up to 5 years.
SELECT name,avg(salary) FROM employees WHERE HIREDATE < (19-july-09) AND
HIREDATE > (19-JUL-09) ''
Is this good?
That's certainly not right - you're asking for employees that have been employed before 19-july-09 and also employed after 19-july-14, so no-one will fulfill both (in fact, no one will fulfil the second condition, since you won't have people you haven't hired yet in the database.
You don't say what database you are using. Also, what do you want out of this exactly? Do you want something like:
Avg salary of employees hired at least 5 years ago = xxxxx
Avg salary of employees hired less than 5 years ago = yyyyy
Just very roughly, this would look something like this (I think this syntax is MySQL compliant):
SELECT CASE WHEN HIREDATE < '2009-07-09' then 1 else 0 END AS five_years,
avg(salary)
FROM employees
GROUP BY CASE WHEN HIREDATE < '2009-07-09' then 1 else 0 END
or alternatively, I think this might work in MySQL to avoid the constants:
SELECT CASE WHEN HIREDATE < date_add(NOW(), INTERVAL -5 YEAR) then 1
ELSE 0 END AS five_years,
avg(salary)
FROM employees
GROUP BY CASE WHEN HIREDATE < date_add(NOW(), INTERVAL -5 YEAR) then 1
ELSE 0 END
I have the following tables:
PROJECTS - project_id, name
EMPLOYEES - employee_id, name
SALARY - employee_id, date, per_hour
HOURS - log_id, project_id, employee_id, date, num_hours
I need to query how much a project is costing. Problem is that Salary can vary. For example, a person can get a raise.
The SALARY table logs the per_hour charge for an employee. With every change in cost being recorded with its date.
How can I query this information to make sure that the the log from the HOURS table is always matched to the right entry from the SALARY table. Right match being.. depending on the date of the hours log, get the row from the salary table with the highest date before the log's date.
ie.. if the work was performed on Feb 14th. Get the row for this employee from the Salary table with the highest date.. but still before the 14th.
Thank you,
What you need is an end date on SALARY. When a new record is inserted into SALARY for an employee, the previous record with the highest date (or better yet, a current flag set to 'Y' as recommended by cletus) should have its end date column set to the same date as the start date for the new record.
This should work with your current schema but be aware that it may be slow.
SELECT
SUM(h.num_hours * s.per_hour) AS cost
FROM PROJECTS p
INNER JOIN HOURS h
ON p.project_id = h.project_id
INNER JOIN (
SELECT
s1.employee_id,
s1.date AS start_date,
MIN(s2.date) AS end_date
FROM SALARY s1
INNER JOIN SALARY s2
ON s1.employee_id = s2.employee_id
AND s1.date < s2.date
GROUP BY
s1.employee_id,
s1.date) s
ON h.employee_id = s.employee_id
AND h.date >= s.start_date
AND h.date < s.end_date
In the 'Hours' table actually log the value of the salary that you use (don't link it based on ID). This will give you more flexibility in the future.
I have found the easiest way to handle queries spanning dates like this is to store a StartDate and an EndDate, where the EndDate is NULL for the current salary. I use a trigger to make sure there is only ever one NULL value for EndDate, and that there are no overlapping date ranges, or gaps between the ranges. StartDate is made not nullable, since that is never a valid value.
Then your join is pretty simple:
select h.num_hours, s.per_hour
from hours h
inner join salary s on h.employee_id = s.employee_id
and h.date >= s.StartDate and (h.date <= s.EndDate or s.EndDate is null)