How to exponential increase every other years salary on a Column #SQL - sql

Im trying to exponential increase every other years salary by %5.
for example: In my table i have 10 rows, which represents 10 years, there is a salary column in that table, i would like to write a code which will increase the salary every year by % 5 : so that it should look like this in every other row.
Year Salary
2014 10000
2015 10500
2016 11025
Anyone can help me with this please, I appreciate your time folks, have a good weekend.

What you have is essentially the compound interest. So use the formula to make it simple:
with years as (
select year from (values (2014),(2015),(2016),(2017)) y(year)
),
starting_salary as (
select cast(10000.0 as float) as salary
)
select
year,
starting_salary.salary * power(cast(1 + 0.05 as float), row_number() over(order by year) - 1)
from
years
cross join starting_salary
order by year;

You can use a recursive CTE to calculate the exponentially increased salaries for all the years in your table:
declare #salaries table (salaryYear int, salary int);
declare #minYear int, #maxYear int;
insert #salaries values (2006, 10000), (2007, NULL), (2008, NULL), (2009, NULL)
, (2010, NULL), (2011, NULL), (2012, NULL), (2013, NULL)
, (2014, NULL), (2015, NULL), (2016, NULL);
select #minYear = min(salaryYear)
, #maxYear = max(salaryYear)
from #salaries;
;with salaries as (
select salaryYear
, salary
from #salaries
where salaryYear = #minYear
union all
select salaryYear + 1
, salary * 105 / 100
from salaries
where salaryYear <= #maxYear
)
select *
from salaries

Similar to Radu, assuming an ID for several workers. The column SALARY is showed just for check
CREATE TABLE SAL (ID INT, YEAR SMALLINT, SALARY NUMERIC(10,2))
INSERT INTO SAL VALUES (1, 2014, 10000)
INSERT INTO SAL VALUES (1, 2015, 10000)
INSERT INTO SAL VALUES (1, 2016, 10000)
INSERT INTO SAL VALUES (1, 2017, 10000)
INSERT INTO SAL VALUES (2, 2014, 20000)
INSERT INTO SAL VALUES (2, 2015, NULL)
INSERT INTO SAL VALUES (2, 2016, NULL)
INSERT INTO SAL VALUES (2, 2017, NULL)
WITH X1 AS
(
SELECT ID, YEAR, SALARY, CAST(SALARY*1.05 AS NUMERIC(10,2)) AS SAL_UPD
FROM SAL
WHERE YEAR=2014
UNION ALL
SELECT A.ID, A.YEAR, A.SALARY, CAST(X1.SAL_UPD*1.05 AS NUMERIC(10,2)) AS SAL_UPD
FROM SAL A
INNER JOIN X1 ON A.YEAR = X1.YEAR+1 AND A.ID=X1.ID
WHERE A.YEAR>2014
)
SELECT * FROM X1
ORDER BY ID, YEAR
Output:
ID YEAR SALARY SAL_UPD
----------- ------ --------------------------------------- ---------------------------------------
1 2014 10000.00 10500.00
1 2015 10000.00 11025.00
1 2016 10000.00 11576.25
1 2017 10000.00 12155.06
2 2014 20000.00 21000.00
2 2015 NULL 22050.00
2 2016 NULL 23152.50
2 2017 NULL 24310.13
Deleting a year, the query for that ID stops:
DELETE FROM SAL WHERE YEAR=2016 AND ID = 2
ID YEAR SALARY SAL_UPD
----------- ------ --------------------------------------- ---------------------------------------
1 2014 10000.00 10500.00
1 2015 10000.00 11025.00
1 2016 10000.00 11576.25
1 2017 10000.00 12155.06
2 2014 20000.00 21000.00
2 2015 NULL 22050.00

Related

Display Average Billing Amount For Each Customer only between years 2019-2021

QUESTION : Display Average Billing Amount For Each Customer ONLY between YEAR(2019-2021).
If customer doesn't have any billing amount for any of the particular year then consider as 0.
-------: OUTPUT :
Customer_ID | Customer_Name | AVG_Billed_Amount
-------------------------------------------------------------------------
1 | A | 87.00
2 | B | 200.00
3 | C | 183.00
--------: EXPLANATION :
If any customer doesn't have any billing records for these 3 years then we need to consider as one record with billing_amount = 0
Like Customer C doesn't have any record for Year 2020, so for C Average will be
(250+300+0)/3 = 183.33 OR 183.00
TEMP TABLE HAS FOLLOWING DATA
DROP TABLE IF EXISTS #TEMP;
CREATE TABLE #TEMP
(
Customer_ID INT
, Customer_Name NVARCHAR(100)
, Billing_ID NVARCHAR(100)
, Billing_creation_Date DATETIME
, Billed_Amount INT
);
INSERT INTO #TEMP
SELECT 1, 'A', 'ID1', TRY_CAST('10-10-2020' AS DATETIME), 100 UNION ALL
SELECT 1, 'A', 'ID2', TRY_CAST('11-11-2020' AS DATETIME), 150 UNION ALL
SELECT 1, 'A', 'ID3', TRY_CAST('12-11-2021' AS DATETIME), 100 UNION ALL
SELECT 2, 'B', 'ID4', TRY_CAST('10-11-2019' AS DATETIME), 150 UNION ALL
SELECT 2, 'B', 'ID5', TRY_CAST('11-11-2020' AS DATETIME), 200 UNION ALL
SELECT 2, 'B', 'ID6', TRY_CAST('12-11-2021' AS DATETIME), 250 UNION ALL
SELECT 3, 'C', 'ID7', TRY_CAST('01-01-2018' AS DATETIME), 100 UNION ALL
SELECT 3, 'C', 'ID8', TRY_CAST('05-01-2019' AS DATETIME), 250 UNION ALL
SELECT 3, 'C', 'ID9', TRY_CAST('06-01-2021' AS DATETIME), 300
-----------------------------------------------------------------------------------
Here, 'A' has 3 transactions - TWICE in year 2020(100+150) and 1 in year 2021(100), but none in 2019(SO, Billed_Amount= 0).
so the average will be calculated as (100+150+100+0)/4
DECLARE #BILL_dATE DATE = (SELECT Billing_creation_date from #temp group by customer_id, Billing_creation_date) /*-- THIS THROWS ERROR AS #BILL_DATE WON'T ACCEPT MULTIPLE VALUES.*/
OUTPUT should look like this:
Customer_ID
Customer_Name
AVG_Billed_Amount
1
A
87.00
2
B
200.00
3
C
183.00
You just need a formula to count the number of missing years.
That's 3 - COUNT(DISTINCT YEAR(Billing_creation_Date)
Then the average = SUM() / (COUNT() + (3 - COUNT(DISTINCT YEAR)))...
SELECT
Customer_ID,
Customer_Name,
SUM(Billed_Amount) * 1.0
/
(COUNT(*) + 3 - COUNT(DISTINCT YEAR(Billing_creation_Date)))
AS AVG_Billed_amount
FROM
#temp
WHERE
Billing_creation_Date >= '2019-01-01'
AND Billing_creation_Date < '2022-01-01'
GROUP BY
Customer_ID,
Customer_Name
Demo : https://dbfiddle.uk/ILcfiGWL
Note: The WHERE clause in another answer here would cause a scan of the table, due to hiding the filtered column behind a function. The way I've formed the WHERE clause allows a "Range Seek" if the column is in an index.
Here is a query that can do that :
select s.Customer_ID, s.Customer_Name, sum(Billed_amount)/ ( 6 - count(1)) as AVG_Billed_Amount from (
select Customer_ID, Customer_Name, sum(Billed_Amount) as Billed_amount
from TEMP
where year(Billing_creation_Date) between 2019 and 2021
group by Customer_ID, year(Billing_creation_Date)
) as s
group by Customer_ID;
According to your description the customer_name C will be 137.5000 not 183.00 since 2018 is not counted and 2020 is not there.

subquery sum and newest record by different type

table employee {
id,
name
}
table payment_record {
id,
type, // 1 is salary, 2-4 is bonus
employee_id,
date_paid,
amount
}
i want to query employee's newest salary and sum(bonus) from some date.
like my payments is like
id, type, employee_id, date_paid, amount
1 1 1 2022-10-01 5000
2 2 1 2022-10-01 1000
3 3 1 2022-10-01 1000
4 1 1 2022-11-01 3000
5 1 2 2022-10-01 1000
6 1 2 2022-11-01 2000
7 2 2 2022-11-01 3000
query date in ['2022-10-01', '2022-11-01']
show me
employee_id, employee_name, newest_salary, sum(bonus)
1 Jeff 3000 2000
2 Alex 2500 3000
which jeff's newest_salary is 3000 becuase there is 2 type = 1(salary) record 5000 and 3000, the newest one is 3000.
and jeff's bonus sum is 1000(type 2) + 1000(type 3) = 2000
the current sql i try is like
select
e.employee_id,
employee.name,
e.newest_salary,
e.bonus
from
(
select
payment_record.employee_id,
SUM(case when type in ('2', '3', '4') then amount end) as bonus,
Max(case when type = '1' then amount end) as newest_salary
from
payment_record
where
date_paid in ('2022-10-01', '2022-11-01')
group by
employee_id
) as e
join
employee
on
employee.id = e.employee_id
order by
employee_id
it's almost done, but the rule of newest_salary is not correct, i just get the max value althought usually the max value is newest record.
The query is below:
SELECT
t1.id employee_id,
t1.name employee_name,
t3.amount newest_salary,
t2.bonus bonus
FROM employee t1
LEFT JOIN
(
SELECT
employee_id,
MAX(CASE WHEN type=1 THEN date_paid END) date_paid,
SUM(CASE WHEN type IN (2,3,4) THEN amount END) bonus
FROM payment_record
WHERE date_paid BETWEEN '2022-10-01' AND '2022-11-01'
GROUP BY employee_id
) t2
ON t1.id=t2.employee_id
LEFT JOIN payment_record t3
ON t3.type=1 AND
t2.employee_id=t3.employee_id AND
t2.date_paid=t3.date_paid
ORDER BY t1.id
db fiddle
I think Postgres is close enough to work with this solution I tested in sql-server, but it should at least be close enough to translate
My approach is to split the payments in the desired range into salary vs bonus, and sum the bonus but use a partitioned row number to identify the newest salary payment for each employee in the desired date range and only join that one to the bonus totals. Note that I used a LEFT JOIN because an employee might not get a bonus.
DECLARE #StartDate DATE = '2022-10-01';
DECLARE #EndDate DATE = '2022-11-01';
with cteSample as ( --BEGIN sample data
SELECT * FROM ( VALUES
(1, 1, 1, CONVERT(DATE,'2022-10-01'), 5000)
, (2, 2, 1, '2022-10-01', 1000)
, (3, 3, 1, '2022-10-01', 1000)
, (4, 1, 1, '2022-11-01', 3000)
, (5, 1, 2, '2022-10-01', 1000)
, (6, 1, 2, '2022-11-01', 2000)
, (7, 2, 2, '2022-11-01', 3000)
) as TabA(Pay_ID, Pay_Type, employee_id, date_paid, amount)
) --END Sample data
, ctePayments as ( --Filter down to just the payments in the date range you are interested in
SELECT Pay_ID, Pay_Type, employee_id, date_paid, amount
FROM cteSample --Replace this with your real table of payments
WHERE date_paid >= #StartDate AND date_paid <= #EndDate
), cteSalary as ( --Identify salary payments in range and order them newest first
SELECT employee_id, amount
, ROW_NUMBER() over (PARTITION BY employee_id ORDER BY date_paid DESC) as Newness
FROM ctePayments as S
WHERE S.Pay_Type = 1
), cteBonus as ( --Identify bonus payments in range and sum them
SELECT employee_id, SUM(amount) as BonusPaid
FROM ctePayments as S
WHERE S.Pay_Type in (2,3,4)
GROUP BY employee_id
)
SELECT S.employee_id, S.amount as SalaryNewest
, COALESCE(B.BonusPaid, 0) as BonusTotal
FROM cteSalary as S --Join the salary list to the bonusa list
LEFT OUTER JOIN cteBonus as B ON S.employee_id = B.employee_id
WHERE S.Newness = 1 --Keep only the newest
Result:
employee_id
SalaryNewest
BonusTotal
1
3000
2000
2
2000
3000

Subquery Factoring recursive sql

Im having an issue where im using recursive subquery factoring to use the previous rows values as my next rows values. Problem is i need to stop using the previous rows values if my product_key changes.
CREATE TABLE MAKE_IT_WORK
(
PRODUCT_KEY NUMBER,
WEEK NUMBER,
OPENING_STOCK NUMBER,
INTAKE NUMBER,
SALES NUMBER,
CLOSING_STOCK NUMBER,
FORWARD_COVER NUMBER
);
Insert into MAKE_IT_WORK (PRODUCT_KEY, WEEK)
Values (1, 1);
Insert into MAKE_IT_WORK (PRODUCT_KEY, WEEK, INTAKE, SALES)
Values (1, 2, 1000, 80);
Insert into MAKE_IT_WORK (PRODUCT_KEY, WEEK, SALES)
Values (1, 3, 70);
Insert into MAKE_IT_WORK (PRODUCT_KEY, WEEK, SALES)
Values (1, 4, 90);
Insert into MAKE_IT_WORK (PRODUCT_KEY, WEEK, SALES)
Values (2, 1, 0);
Insert into MAKE_IT_WORK (PRODUCT_KEY, WEEK, INTAKE, SALES)
Values (2, 2, 6000, 500);
Insert into MAKE_IT_WORK (PRODUCT_KEY, WEEK, SALES)
Values (2, 3, 70);
Insert into MAKE_IT_WORK (PRODUCT_KEY, WEEK, SALES)
Values (2, 4, 350);
CURRENT QUERY
with master
as(select product_key,week,opening_stock ,intake,sales,closing_stock,forward_cover,row_number()over( order by 1) lvl,product_key-1 pkey
from make_it_work),
bdw_knows_best(product_key,week,opening_stock,intake,sales,closing_stock,forward_cover,lvl,pkey) as
(select product_key
,week
,opening_stock
,nvl(intake,0)intake
,sales
,closing_stock
,forward_cover
,lvl
,pkey
from master
where lvl = 1
union all
select a.product_key
,a.week
,case when b.closing_stock < 0 then 0
else b.closing_stock
end opening_stock
,nvl(a.intake,0)intake
,nvl(a.sales,0) sales
,case when nvl(b.closing_stock,0) + nvl(a.intake,0) - nvl(a.sales,0) < 0 THEN 0
else nvl(b.closing_stock,0) + nvl(a.intake,0) - nvl(a.sales,0)
end closing_stock
,a.forward_cover
,b.lvl +1
,a.pkey pkey
from master a,
bdw_knows_best b
where a.lvl = b.lvl +1
)
select product_key,week,opening_stock,intake,sales,closing_stock,forward_cover,lvl,pkey from bdw_knows_best;
REQUIRED
When the product key changes from 1 to 2, I need to use the values from Product_Key 2 and not the last records from Product_Key 1. I need to somehow group the by Product_Key buckets(so to speak).
Any help or ideas would be highly appreaciated
You don't need a recursive CTE. Window functions (the OVER clause) will produce the result you want. For example:
select product_key, week, opening_stock, intake, sales,
coalesce(opening_stock, 0)
+ sum(intake) over(partition by product_key order by week)
- sum(sales) over(partition by product_key order by week)
as closing_stock
from make_it_work
order by product_key, week;
Result:
PRODUCT_KEY WEEK OPENING_STOCK INTAKE SALES CLOSING_STOCK
------------ ----- -------------- ------- ------ -------------
1 1
1 2 1000 80 920
1 3 70 850
1 4 90 760
2 1 0
2 2 6000 500 5500
2 3 70 5430
2 4 350 5080
See running example at db<>fiddle.

SQL turn rows into columns

Hello is it possible to turn row values into columns.
I am using ORACLE SQL and I want to take the month and turn it into columns with the kpi value as shown below.
I tried partitions and merge statements but nothing seems to work.
I would really appreciate some help.
Thank you in advance.
Input data:
department
year
month
kpi
value
A
2000
1
sales
5000
A
2000
1
revenue per client
120
A
2000
2
sales
6000
A
2000
2
revenue per client
140
Desired Output:
department
year
kpi
1
2
A
2000
sales
5000
6000
A
2000
revenue per client
120
140
You can use pivot to do so:
Schema and insert statements:
create table mytable (department varchar(20),year int,month int,kpi varchar(50),value int);
insert into mytable values('A', 2000, 1, 'sales' ,5000);
insert into mytable values('A', 2000, 1, 'revenue per client', 120);
insert into mytable values('A', 2000, 2, 'sales' ,6000);
insert into mytable values('A', 2000, 2, 'revenue per client', 140);
Query:
select * from (
select department,year,month,kpi,value
from mytable
)
pivot
(
max(value)
for month in (1,2)
)
Output:
DEPARTMENT
YEAR
KPI
1
2
A
2000
revenue per client
120
140
A
2000
sales
5000
6000
db<fiddle here
You can use conditional aggrwegation:
select department, year, kpi,
max(case when month = 1 then value end) as month_1,
max(case when month = 2 then value end) as month_2
from t
group by department, year, kpi;

Running count for each 2 rows

I am trying to calculate running count for each 2 rows like below,
CREATE TABLE sales
(
EmpId INT,
Yr INT,
Sales DECIMAL(8,2)
)
INSERT INTO sales (EmpId, Yr, Sales)
VALUES (1, 2005, 12000), (1, 2006, 18000), (1, 2007, 25000),
(1, 2008, 25000), (1, 2009, 25000),
(2, 2005, 15000), (2, 2006, 6000), (2, 2007, 6000)
SELECT
EmpId, Yr, sales,
SUM(Sales) OVER (PARTITION BY empid ORDER BY empid ROWS BETWEEN 2 PRECEDING AND CURRENT ROW ) AS TotalSales2
FROM
sales
Output:
EmpId Yr sales TotalSales2
-----------------------------------
1 2005 12000 12000
1 2006 18000 30000
1 2007 25000 55000
1 2008 25000 68000
1 2009 25000 75000
2 2005 15000 15000
2 2006 6000 21000
2 2007 6000 27000
But expected output:
EmpId Yr Sales TotalSales2
-----------------------------------
1 2005 12000 12000
1 2006 18000 30000
1 2007 25000 25000
1 2008 25000 50000
1 2009 25000 25000
2 2005 15000 15000
2 2006 6000 21000
2 2007 6000 6000
What am I doing wrong in this query?
Note: SQL Servre version is 2012.
SELECT EmpId, Yr, Sales,
CASE WHEN ROW_NUMBER() OVER (PARTITION BY EmpId ORDER BY yr) % 2 = 0
THEN sales + lag(sales, 1, 0) OVER (PARTITION BY empid ORDER BY yr)
ELSE sales
END AS TotalSales2
FROM sales
Lag returns the previous row's value - when row_number() is even, add the current row's value to the previous row - otherwise, just show the sales for the current row. Partition each by EmpId, order each by yr - output matches the expected.
Also, thanks so much for adding the DDL/sample data.
The expression:
SUM(Sales) OVER (PARTITION BY empid
ORDER BY empid
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
calculates the sum considering the current row and the 2 rows immediately preceding it. So it actually calculates a rolling sum, which is what you really don't want.
I think you are actually looking for something like the following:
;WITH CTE_Group AS (
SELECT EmpId, Yr, sales,
(ROW_NUMBER() OVER (PARTITION BY empid ORDER BY yr) + 1 ) / 2 AS grp
FROM sales
)
SELECT EmpId, Yr, sales,
SUM(sales) OVER (PARTITION BY empid, grp
ORDER BY yr) AS TotalSales2
FROM CTE_Group
The above query uses a CTE in order to calculate field grp: the value of this field is 1 for the first two records of an empid partition, 2 for the next two records, and so on.
Using grp we can calculate the running total of sales for groups of 2 as is the requirement of the OP.
Demo here
Edit:
To offset a larger group of records try using (credit goes to #Max Szczurek for pointing this out):
(ROW_NUMBER() OVER (PARTITION BY empid ORDER BY yr) - 1 ) / n AS grp
where n is the number of records each group contains.
Although answer is already accepted, consider below query also. This will give the required output :
DECLARE #sales TABLE(EmpId INT, Yr INT, Sales DECIMAL(8,2))
INSERT INTO #sales ( EmpId, Yr, Sales )
VALUES (1, 2005, 12000),
(1, 2006, 18000),
(1, 2007, 25000),
(1, 2008, 25000),
(1, 2009, 25000),
(2, 2005, 15000),
(2, 2006, 6000),
(2, 2007, 6000)
;WITH SAMPLE_DATA
AS
(
SELECT ROW_NUMBER()over(partition by empid order by (select 100))SNO,* FROM #Sales
)
SELECT EmpId,Yr,Sales
,CASE WHEN (SNO%2=0)
THEN SALES+
(
SELECT Sales FROM SAMPLE_DATA T2 WHERE T2.EmpId=T1.EmpId AND T2.SNO=T1.SNO-1
)
ELSE Sales END
TotalSales2
FROM SAMPLE_DATA T1
OUTPUT
--------------------------------------
--EmpId Yr Sales TotalSales2
--------------------------------------
1 2005 12000.00 12000.00
1 2006 18000.00 30000.00
1 2007 25000.00 25000.00
1 2008 25000.00 50000.00
1 2009 25000.00 25000.00
2 2005 15000.00 15000.00
2 2006 6000.00 21000.00
2 2007 6000.00 6000.00
--------------------------------------