Query for current salary based on date - mysql

id name salary date
1 aaa 10000 10/2/2014
1 aaa 15000 06/04/2015
1 aaa 20000 16/07/2016
1 aaa 25000 10/04/2017
If an employee got salary hike every year,
how can I get an employee's current year salary?
That query should be working fine for every year.
If I execute it in 2013 it displays 2013 year salary, if execute in 2016 it displays 2016 year salary, similarly, for 2017 also it would be display 2017 salary.

Check this SQL Fiddle for MySQL
SELECT
SUM(salary) as salary
FROM salary
WHERE YEAR(STR_TO_DATE(date, '%d/%m/%Y')) = YEAR(CURDATE())
GROUP BY id

in Mysql you can do like this:
select * form tablename where YEAR(now()) = YEAR(fieldname)

Solution for ORACLE
SELECT *
FROM SALARY_TABLE
WHERE NAME = 'whatever_the_name_is'
AND TRUNC(HIKE_DATE,'YYYY') = TRUNC(SYSDATE,'YYYY');
Sample query with dummy data
WITH SALARY_TABLE AS -- Sample data queried from DUAL
(SELECT 1 AS ID,
'aaa' AS name,
10000 AS salary,
TO_DATE('10/2/2014', 'dd/mm/yyyy') AS hike_date
FROM dual
UNION
SELECT 2, 'aaa', 15000, TO_DATE('06/04/2015', 'dd/mm/yyyy') FROM dual
UNION
SELECT 3, 'aaa', 20000, TO_DATE('16/07/2016', 'dd/mm/yyyy') FROM dual
UNION
SELECT 3, 'aaa', 25000, TO_DATE('10/04/2017', 'dd/mm/yyyy') FROM dual
)
SELECT * -- Actual solution
FROM SALARY_TABLE
WHERE NAME = 'aaa'
AND TRUNC(HIKE_DATE,'YYYY') = TRUNC(SYSDATE,'YYYY');

Related

Generating accurate subtotals on fanout joins

My data model:
The query:
SELECT
ProductSummary.Product,
ProductSummary.ID AS SummaryID,
Transactions.DateOfSale,
Summary.Revenue
FROM
ProductSummary JOIN
Transactions ON (Transactions.ProductID = ProductSummary.ID)
WHERE
Transactions.DateOfSale < '2014-01-10'
The data itself looks fine, however I also want to show a subtotal, and the subtotal of a table should be the amount displayed when that table is not joined.
For example, for subtotaling Revenue the answer should always be what I would get from SELECT SUM(Revenue) FROM Summary (after applying any necessary filters). How to generate that?
One way to do this would be using an analytic function to count the unique rows while totaling, for example:
WITH
ProductSummary (Product, ID, Revenue) AS (
SELECT 'Car', 1, 12 UNION ALL
SELECT 'Phone', 2, 7
),
Transactions (SummaryID, ID, DateOfSale) AS (
SELECT 1, 1, DATE '2014-01-01' UNION ALL
SELECT 1, 2, DATE '2014-01-02' UNION ALL
SELECT 1, 3, DATE '2014-01-03' UNION ALL
SELECT 2, 4, DATE '2014-01-04' UNION ALL
SELECT 1, 5, DATE '2014-01-04' UNION ALL
SELECT 1, 6, DATE '2014-01-04' UNION ALL
SELECT 1, 7, DATE '2020-01-01'
)
SELECT
ProductSummary.Product,
ProductSummary.ID AS SummaryID,
Transactions.DateOfSale,
ProductSummary.Revenue,
IF(
ROW_NUMBER() OVER (PARTITION BY ProductSummary.ID) = 1,
ProductSummary.Revenue,
0
) RevenueUnique
FROM
ProductSummary
JOIN Transactions ON (Transactions.SummaryID=ProductSummary.ID)
WHERE
Transactions.DateOfSale < DATE '2014-01-10';

Get employees who received a raise in 2 consecutive years

I am trying to Get employees who received a raise in 2 consecutive years, in this case employee 1000 is the right answer.
here is the data and the sql i have tried.
EID
SALARY
YEAR
1000
10,000
2016
1000
7,500
2015
1000
6,000
2014
1001
8,000
2016
1001
7,500
2015
1002
7,500
2016
1002
7,500
2015
1002
5,000
2014
1003
6,000
2016
1003
7,000
2015
1003
5,000
2014
i have used following code however it gets only row number by EID and not calcualtion of last year and present year, i need to find employee who got raise in 2 consecutive years.
output
select * ,
row_number() over(partition by eid order by salary and year desc)as rn
from gs;
You can do it using the LEAD window function that compute the two consecutive previous value of the salary. Then you can check how many employees have at least one row with salary1 < salary2 < salary3.
SELECT DISTINCT
eid
FROM (
SELECT
eid,
year,
salary,
(LEAD(salary, 1) OVER(PARTITION BY eid ORDER BY year DESC)) AS prev_salary1,
(LEAD(salary, 2) OVER(PARTITION BY eid ORDER BY year DESC)) AS prev_salary2
FROM
employees
) consecutive3
WHERE
salary > prev_salary1
AND
prev_salary1 > prev_salary2
The assumption is that there are no missing years for which a salary of a dependent was not recorded.
Here's the fiddle: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=8c0d8a1deec8e77bb32a173656c3e386.
EDIT: Detailed explanation
Let's do the example of Jennifer, who has worked for five years and got these salaries:
2018 -> 65000
2017 -> 55000
2016 -> 50000
She's a candidate for being selected as her salary was raised three times consecutively.
1. LEAD(salary, 1) OVER(PARTITION BY eid ORDER BY year DESC)
Allows you to get the salary for year "Y" and the salary for year "Y-1":
("year" -> "salary", "previous_salary")
2018 -> 65000 , 55000
2017 -> 55000 , 50000
2016 -> 50000 , NULL
2. LEAD(salary, 2) OVER(PARTITION BY eid ORDER BY year DESC)
Allows you to get the salary for year "Y" and the salary for year "Y-1":
("year" -> "salary", "previous_salary", "previous_salary_by_2_years")
2018 -> 65000 , 55000 , 50000
2017 -> 55000 , 50000 , NULL
2016 -> 50000 , NULL , NULL
3. WHERE salary > prev_salary1 AND prev_salary1 > prev_salary2
Some filtering on the employees who
have their year3 salary higher than their year2 salary (salary > prev_salary1)
have their year2 salary higher than their year1 salary (prev_salary1 > prev_salary2)
I know that this has already been answered but here is my take using the lag function to determine if there was an increase from the previous year and ran that twice.
SELECT *
FROM (
SELECT
t2.*,
LAG(increase) over (partition by eid order by year) AS increaseNextYear
FROM (
SELECT
t1.*,
COALESCE(salary - LAG(salary) over (partition by eid order by year), 0) > 0 AS increase
FROM tbl_test t1
) as t2
) t3 where increase AND increaseNextYear
with
dates as
(
select
a.*,
dense_rank() OVER (
partition by eid
order by year desc, salary
)
as rn,
case
when
lead(salary,2)over(partition by eid order by year, salary)
>
lead(salary,1)over(partition by eid order by year, salary)
and
lead(salary,1)over(partition by eid order by year, salary)
>
salary
then
1
else
0
end
as flag
from
employees a
)
select
eid
from
dates
where
rn = 3
and flag = 1
Not a beautiful query, but straight-forward: find employees who had a salary in a year where the salary in the previous year was lower and the salary in the year before that even lower. Using LAG is more elegant, but I thought I'd throw this in, just to show an alternative.
select *
from employee
where exists
(
select null
from gs
where gs.eid = employee.id
and exists
(
select null
from gs prev
where prev.eid = gs.eid
and prev.year = gs.year - 1
and prev.salary < gs.salary
and exists
(
select null
from gs prevprev
where prevprev.eid = prev.eid
and prevprev.year = prev.year - 1
and prevprev.salary < prev.salary
)
)
);
Same thing with a join:
select *
from employee
where exists
(
select null
from gs
join gs prev on prev.eid = gs.eid
and prev.year = gs.year - 1
and prev.salary < gs.salary
join gs prevprev on prevprev.eid = prev.eid
and prevprev.year = prev.year - 1
and prevprev.salary < prev.salary
where gs.eid = employee.id
);
For versions prior to 8.0 (mine is 5.7) which lack the cutting edge features of the newer one, I tried a procedure to accomplish the job. First and foremost, get all the eid which have no less than three years' salary record, which is the minimum requirement of the consecutive bonus. The rest is to fetch and compare using a cursor from the eid pool. The result is stored in a temporary table t .
delimiter //
drop procedure if exists lucky_emp//
create procedure lucky_emp()
begin
declare old_eid int default 0;
declare old_salary int;
declare new_eid int ;
declare new_salary int;
declare bonus_year int;
declare fin bool default false;
declare c cursor for select eid,salary from salary where eid in(select eid from salary group by eid having count(eid)>=3) order by eid,year;
declare continue handler for not found set fin=true;
drop temporary table if exists t ;
create temporary table t (t_eid int);
open c;
lp:loop
fetch c into new_eid ,new_salary;
if fin=true then
leave lp;
end if;
if new_eid !=old_eid then
set old_eid=new_eid,old_salary=0,bonus_year=0;
end if;
if new_salary> old_salary then
set bonus_year=bonus_year+1,old_salary=new_salary;
else
set bonus_year=0;
end if;
if bonus_year=3 and new_eid not in(select t_eid from t) then
insert t values(new_eid);
end if;
end loop lp;
end//
delimiter ;
select * from t ;
Select a.*, b.prev_sal1, b.prev_sal2
from employees a
join (
Select eid ,year,
lag(salary,1) over (partition by eid order by year) as prev_sal1,
lag(salary,2) over (partition by eid order by year) as prev_sal2
from employees ) b
on a.eid=b.eid
and a.year = b.year
where salary>prev_sal1 and prev_sal1>prev_sal2
fiddle: https://dbfiddle.uk/rfGv31zM

How to rewrite the following into an EXISTS

I have the following SQL statement that I would like to convert to use an EXISTS. How would this be done?
with Sales as (
select 'Office Supplies' Category , 2014 Year,22593.42 Profit UNION all
select 'Technology', 2014, 21492.83 UNION all
select 'Furniture', 2014, 5457.73 UNION all
select 'Office Supplies', 2015, 25099.53 UNION all
select 'Technology', 2015, 33503.87 UNION all
select 'Furniture', 2015, 50000.00 UNION all
select 'Office Supplies', 2016, 35061.23 UNION all
select 'Technology', 2016, 39773.99 UNION all
select 'Furniture', 2016, 6959.95
)
select Category, Profit - LAG(Profit) OVER (PARTITION BY Category ORDER BY Year) Diff
FROM Sales where 1=1 qualify Diff < 0
In other words, I want the query to be something like:
SELECT * FROM tbl WHERE EXISTS (...)
If LAG exists then you can use a sub-query instead of Teradata's QUALIFY.
SELECT Category, `Year`, Profit
, PreviousProfit
, (Profit - PreviousProfit) AS Diff
FROM
(
SELECT Category, `Year`, Profit
, LAG(Profit) OVER (PARTITION BY Category ORDER BY `Year`) AS PreviousProfit
FROM Sales AS sale
) AS sale
WHERE EXISTS (
SELECT 1
FROM Sales AS sale2
WHERE sale2.Category = sale.Category
AND sale2.Year = sale.Year - 1
AND sale2.Profit > sale.Profit
)
AND PreviousProfit > Profit
The EXISTS isn't really needed then.

Select MAX ID of the month

I have a list of ID’s Shown below.
AT0920130004
AT0920130005
AT0920130006
AT0920130007
AT0920130008
AT0920130009
AT0920130010
AT1020130001
AT1020130002
AT1020130003
AT1020130004
AT1020130005
AT1120130003
AT1120130004
AT1120130005
AT1120130006
Here an example record has the format ATmmyyyyxxxx.
where
AT represents location,
mm represents month eg 10 would be October
yyyy represents year eg. 2013
xxxx represents the increasing seed of numbers.
Now I need to select an ID which is generated in the end of the month.
For ex: last id of September i.e. AT0920130010.
Any suggestions are appreciated.
You can use this query to get max ID for each month and year
select substring([id], 3, 6) MonthYear, max([id]) MaxID
from yourtable
group by substring([id], 3, 6)
To get max ID for one specific month, you can use this query:
select max([id])
from yourtable
where cast(substring([id], 5, 4) as int) = 2013 -- year
and cast(substring([id], 3, 2) as int) = 9 -- month
try this :
select MAX(t.ID) from TableName t
group by SUBSTRING(t.ID, 3, 2)
You basically want to get the max id that matches a location, a month and a year. Something like this should do it:
SELECT max(id)
FROM myTable
WHERE substring(id, 0, 2) = [location]
AND susbstring(id, 2, 2) = [month]
AND substring(id, 4, 4) = [year]
try this(this will return whole field, and just id):
SELECT YOUR_FIELD, MAX(RIGHT(YOUR_FIELD,4)) AS just_id FROM YOUR_TABLE WHERE RIGHT(YOUR_FIELD,LENGTH(YOUR_FIELD)-2) LIKE '09%'

SELECT de-normalized columns into separate records?

I am playing around with SQL a little just so I am not completely ignorant about it if I am ever asked in a job interview. My friend was recently asked the following question at an interview and he couldn't get it and I asked somebody at work who knows SQL decently and he didn't know. Can you guys answer this problem for me and then explain how it works? Please?
*The problem*
Database normalization (or lack of normalization) often presents a challenge for developers.
Consider a database table of employees that contains three fields:
EmployeeID
EmployeeName
EmailAddresses
Every employee, identified by a unique EmployeeID, may have one or more comma-separated, #rockauto.com email address(es) in the EmailAddresses field.
The database table is defined below:
CREATE TABLE Employees
(
EmployeeID int UNSIGNED NOT NULL PRIMARY KEY,
EmployeeName varchar(50) NOT NULL,
EmailAddresses varchar(40) NOT NULL ,
PRIMARY KEY(EmployeeID)
);
For testing purposes, here is some sample data:
INSERT INTO Employees (EmployeeID, EmployeeName, EmailAddresses) VALUES
('1', 'Bill', 'bill#companyx.com'),
('2', 'Fred', 'fred#companyx.com,freddie#companyx.com'),
('3', 'Fred', 'fredsmith#companyx.com'),
('4', 'Joe', 'joe#companyx.com,joe_smith#companyx.com');
Your task is to write a single MySQL SELECT query that will show the following output for the sample data above:
Employee EmailAddress
Bill bill#companyx.com
Fred (2) fred#companyx.com
Fred (2) freddie#companyx.com
Fred (3) fredsmith#companyx.com
Joe joe#companyx.com
Joe joe_smith#companyx.com
Please take note that because there is more than one person with the same name (in this case, "Fred"), the EmployeeID is included in parenthesis.
Your query is required to written in MySQL version 5.1.41 compatible syntax. You should assume that the ordering is accomplished using standard database ascending ordering: "ORDER BY EmployeeID ASC"
For this problem, you need to submit a single SQL SELECT query. Your query should be able to process a table of 1000 records in a reasonable amount of time.
only if you have less than 10000 emails.... is that acceptable?
select
if(t1.c > 1, concat(e.employeename, ' (', e.employeeid, ')'), e.employeename) as Employee,
replace(substring(substring_index(e.EmailAddresses, ',', n.row), length(substring_index(e.EmailAddresses, ',', n.row - 1)) + 1), ',', '') EmailAddress
from
(select employeename, count(*) as c from Employees group by employeename) as t1,
(select EmployeeID, length(EmailAddresses) - length(replace(EmailAddresses,',','')) + 1 as emails from Employees) as t2,
(SELECT #row := #row + 1 as row FROM
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) x,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) x2,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) x3,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) x4,
(SELECT #row:=0) as ff) as n,
Employees e
where
e.employeename = t1.employeename and
e.employeeid = t2.employeeid and
n.row <= t2.emails
order by e.employeeid;
EDIT:
With less useless numbers generated:
select
if(t1.c > 1, concat(e.EmployeeName, ' (', e.EmployeeID, ')'), e.EmployeeName) as Employee,
replace(substring(substring_index(e.EmailAddresses, ',', n.row), length(substring_index(e.EmailAddresses, ',', n.row - 1)) + 1), ',', '') as EmailAddress
from
(select EmployeeName, count(*) as c from Employees group by EmployeeName) as t1,
(select EmployeeID, length(EmailAddresses) - length(replace(EmailAddresses,',','')) + 1 as emails from Employees) as t2,
(select `1` as row from (select 1 union all select 2 union all select 3 union all select 4) x) as n,
Employees e
where
e.EmployeeName = t1.EmployeeName and
e.EmployeeID = t2.EmployeeID and
n.row <= t2.emails
order by e.EmployeeID;
And what did we learn? Poor database design results awful queries. And you can do stuff with SQL, that are probably supported only because people do poor database designs... :)