SQL turn rows into columns - sql

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;

Related

How can I get cumulative sum using SQL?

I have a table of individual sales, for which I would like to summarize into two columns, with a monthly total in one and a cumulative sum in another.
Company A and Company B are subsidiary company under the same parent company, thus, need to be considered as one for calculating cumulative income.
I tried this code and output is following:
SUM(INCOME) OVER(PARTITION BY COMPANY ORDER BY MONTH ROWS UNBOUNDED PRECEDING) AS CUMULATIVE
Company Month Income Cumulative
Company A 1 20 20
Company B 1 0 20
Company C 1 20 20
Company A 2 20 40
Company B 2 0 40
But I want to return 0 when Company B has 0 income for cumulative
Company Month Income Cumulative
Company A 1 20 20
Company B 1 0 0
Company C 1 20 20
Company A 2 20 40
Company B 2 0 0
How can I return 0 for cumulative when either company A or company B has income of 0?!
You have a bad design for the prupose you want and need
adding an identidy field for sorting purposes and converting your short name into numbers, you can achieve it with window function.
A column for the year is also necessary as you can't sort when you got two janissary for the same company
CREATE TABLE tabl1 (
id_num int IDENTITY(1,1),
Company VARCHAR(9),
Month VARCHAR(3),
Income INTEGER
);
INSERT INTO tabl1
(Company, Month, Income)
VALUES
('Company A', 'Jan', '20'),
('Company B', 'Jan', '0'),
('Company A', 'Feb', '20'),
('Company B', 'Feb', '0'),
('Company C', 'Jan', '20');
5 rows affected
SELECT
Company, Month, Income,
SUM(Income) OVER(PARTITION BY company ORDER BY
CASE WHEN Month= 'Jan' THEN 1
WHEN Month = 'Feb' THEN 2
WHEN Month = 'Dec' THEN 12
END) as Cumulative
FROM
tabl1
ORDER BY id_num
Company
Month
Income
Cumulative
Company A
Jan
20
20
Company B
Jan
0
0
Company A
Feb
20
40
Company B
Feb
0
0
Company C
Jan
20
20
fiddle
So a better would be
CREATE TABLE tabl1 (
id_num int IDENTITY(1,1),
Company VARCHAR(9),
Month VARCHAR(3),
[Year] int,
Income INTEGER
);
INSERT INTO tabl1
(Company, Month,[Year], Income)
VALUES
('Company A', 'Jan',2023, '20'),
('Company B', 'Jan',2023, '0'),
('Company A', 'Feb',2023, '20'),
('Company B', 'Feb',2023, '0'),
('Company C', 'Jan',2023, '20');
5 rows affected
SELECT
Company, Month,[Year], Income,
SUM(Income) OVER(PARTITION BY company ORDER BY
CASE WHEN Month= 'Jan' THEN 1
WHEN Month = 'Feb' THEN 2
WHEN Month = 'Dec' THEN 12
END, YEAR ) as Cumulative
FROM
tabl1
ORDER BY id_num
Company
Month
Year
Income
Cumulative
Company A
Jan
2023
20
20
Company B
Jan
2023
0
0
Company A
Feb
2023
20
40
Company B
Feb
2023
0
0
Company C
Jan
2023
20
20
fiddle

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

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
--------------------------------------

How to exponential increase every other years salary on a Column #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

Performing a COUNT on the MAX number of occurences

My scenario is that person A can sells product A each month of the year. From that information I had to calculate in what month they sold the most of product A for the current year.
Should they sell 10 of product A in January, 6 in August and 10 October, i take the info for the latest month (in this case October).
However, i want to include some sort of tracker that says if the current MAX for the year has been equaled by person A at an earlier point in the year i want to COUNT the number of occurencies. Should person A go on to sell 15 in Novemeber, the counter should restart.
Current data =
EMP PRODUCT MONTH VOLUME
---------------------------------------------------
A A 1 10
A A 8 6
A A 10 10
AIM=
EMP PRODUCT MAX(VOLUME) COUNT
---------------------------------------------------
A A 10 2
Any suggestions as to the most efficient way of resolving this would be great!
CREATE TABLE MY_TABLE (EMP VARCHAR2(10), PRODUCT VARCHAR2(10), MONTH NUMBER, VOLUME NUMBER);
INSERT INTO MY_TABLE VALUES ('A', 'A', 1, 10);
INSERT INTO MY_TABLE VALUES ('A', 'A', 8, 6);
INSERT INTO MY_TABLE VALUES ('A', 'A', 10, 10);
COMMIT;
--EMP PRODUCT MONTH VOLUME
-----------------------------------------------------
--A A 1 10
--A A 8 6
--A A 10 10
SELECT EMP,
PRODUCT,
VOLUME,
MY_COUNT
FROM ( SELECT EMP,
PRODUCT,
VOLUME,
COUNT (MY_RANK) MY_COUNT,
RANK () OVER (PARTITION BY EMP, PRODUCT ORDER BY VOLUME DESC)
MY_SECOND_RANK
FROM (SELECT EMP,
PRODUCT,
volume,
RANK ()
OVER (PARTITION BY EMP, PRODUCT
ORDER BY VOLUME DESC, MONTH DESC)
MY_RANK
FROM MY_TABLE)
GROUP BY EMP, PRODUCT, VOLUME)
WHERE MY_SECOND_RANK = 1;