Running count for each 2 rows - sql

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

Related

How use select * with group by subquery in sql server

I have table of employees salary details records with columns
Id Name Year Month Salary
1 ABC 2021 Jan 50000
2 PQR 2021 Jan 40000
3 KLM 2021 Feb 45000
4 LMN 2021 Jan 55000
5 LMN 2022 Jan 20000
6 ABC 2022 Feb 25000
7 ABC 2022 Jan 2500
8 ABC 2022 Dec 60000
9 LMN 2022 Nov 70000
Now I want to find which employee gets salary greater than 100000 from joining, and display employees all data
--find which employee gets more than 100000 salary till now
select name,sum(salary) as AnnualSalary from tblEmpsalary
group by Name
having sum(Salary)>100000 --this query works
--but below query display no data , (I want to show all data of employee which gets more than 100000 total salary)
SELECT id, name,Month,Year, SUM(Salary) AS TotalSales
FROM tblEmpsalary
GROUP BY name,Id,Month,Year,Salary
having SUM(Salary)>100000;
SELECT T.ID,T.Name,T.Year,T.Month,T.Salary
FROM tblEmpsalary AS T
JOIN
(
select ID
from tblEmpsalary
group by ID
having sum(Salary)>100000
)AS X ON T.ID=X.ID
You can use a window function for this
SELECT
id,
name,
Month,
Year,
TotalSales
FROM (
SELECT *,
SUM(Salary) OVER (PARTITION BY name) AS TotalSales
FROM tblEmpsalary e
) e
WHERE e.TotalSales > 100000;
Please, try with below query where one query for grouping and another is joining for fetch employees details:
SELECT TS.id, TS.name, TS.Month,Year, TS.Salary, ATS.TotalSales FROM
(SELECT Month, Year, SUM(Salary) AS TotalSales
FROM tblEmpsalary
GROUP BY Month,Year,Salary
HAVING SUM(Salary)>100000
) AS ATS
LEFT OUTER JOIN tblEmpsalary TS on ATS.Month = TS.Month and ATS.Year = TS.Year
ORDER BY TS.name, TS.Id, TS.Month, TS.Year, ATS.TotalSales

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;

How to get sum of first three rows then next three rows in a new column

hello i have a sql server inner join
SELECT a.field_name,
b.ticker AS Ticker_Name,
Year(c.date) AS YEAR,
c.stock_id,
c.field_id,
Sum(c.value) AS Value
FROM field_table a
INNER JOIN stock_transaction c
ON a.id = c.field_id
INNER JOIN stocks b
ON b.id = c.stock_id
WHERE b.id = 230
AND c.field_id = 29
GROUP BY Year(c.date),
b.ticker,
c.stock_id,
c.field_id,
a.field_name;
I have also attached my output .
my output is sum of sales value year wise . now my task is i have to show sum of value of three rows in a new column . example: 2008 , 2009 , 2010 then again sum of value 2011 , 2012 , 2013 . then again 2014 , 2015 ,2016
my desire output is
can anyone suggest me how can i achieve this .thanks in advance
Do you just want lag()?
SELECT f.field_name, s.ticker AS Ticker_Name,
Year(st.date) AS YEAR, st.stock_id, st.field_id,
Sum(st.value) AS Value,
LAG(Sum(st.value, 1)) OVER (PARTITION BY f.field_name, s.ticker, st.stock_id, st.field_id ORDER BY Year(st.date)) as year_1,
LAG(Sum(st.value, 2)) OVER (PARTITION BY f.field_name, s.ticker, st.stock_id, st.field_id ORDER BY Year(st.date)) as year_2,
LAG(Sum(st.value, 3)) OVER (PARTITION BY f.field_name, s.ticker, st.stock_id, st.field_id ORDER BY Year(st.date)) as year_3
FROM field_table f INNER JOIN
stock_transaction st
ON f.id = st.field_id INNER JOIN
stocks s
ON s.id = st.stock_id
WHERE s.id = 230 AND st.field_id = 29
GROUP BY Year(st.date), s.ticker, st.stock_id, st.field_id, f.field_name;
Notice that I also replaced the table aliases with abbreviations for the tables. You should use meaningful table aliases -- such as abbreviations -- rather than arbitrary letters.
This answer is just a basic example that demonstrates how to sum the values from each three rows as a new column in your result set. You can change this to fit your needs. Example uses ROW_NUMBER() and integer division to calculate each group number.
Input:
Year Value
2008 100.00
2009 200.00
2010 300.00
2011 400.00
2012 500.00
2013 600.00
2014 700.00
2015 800.00
2016 900.00
2017 1000.00
2018 1100.00
Statement:
-- Table
CREATE TABLE #Items (
[Year] int,
[Value] numeric(20, 2)
)
INSERT INTO #Items
([Year], [Value])
VALUES
(2008, 100),
(2009, 200),
(2010, 300),
(2011, 400),
(2012, 500),
(2013, 600),
(2014, 700),
(2015, 800),
(2016, 900),
(2017, 1000),
(2018, 1100)
-- Statement
;WITH cte AS (
-- Generate group numbers. Each group is every tree years.
SELECT
[Year],
[Value],
(ROW_NUMBER() OVER (ORDER BY [Year]) - 1) / 3 AS GroupNumber,
ROW_NUMBER() OVER (ORDER BY [Year]) AS RowNumber
FROM #Items
), sums AS (
-- Get total sums for each group
SELECT
SUM([Value]) AS [SumValue],
GroupNumber,
ROW_NUMBER() OVER (ORDER BY GroupNumber) AS RowNumber
FROM cte
GROUP BY [GroupNumber]
)
-- Final SELECT
SELECT cte.[Year], cte.[Value], sums.[SumValue]
FROM cte
LEFT JOIN sums ON (cte.RowNumber = sums.RowNumber)
Output:
Year Value SumValue
2008 100.00 600.00
2009 200.00 1500.00
2010 300.00 2400.00
2011 400.00 2100.00
2012 500.00 NULL
2013 600.00 NULL
2014 700.00 NULL
2015 800.00 NULL
2016 900.00 NULL
2017 1000.00 NULL
2018 1100.00 NULL

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

Aggregation per Date

I have thousands of companies listed but for illustration; I cited 2 companies. I need to produce the column TotalSales in which values are the sum of sales per company , a year prior to its corresponding actual year & quarter.
Company Sales Quarter Year TotalSales QtrYr_Included
ABC Inc. 10,000 1 2010 null Q12009 - Q42009
ABC Inc. 50,000 2 2010 10,000 Q22009 - Q12010
ABC Inc. 35,000 3 2010 60,000 Q32009 - Q22010
ABC Inc. 15,000 4 2010 95,000 Q42009 - Q32010
ABC Inc. 5,000 1 2011 110,000 Q12010 - Q42010
ABC Inc. 10,000 2 2011 105,000 Q22010 - Q12011
SoKor Group 50,000 1 2009 null Q12008 - Q42008
SoKor Group 10,000 2 2009 50,000 Q22008 - Q12009
SoKor Group 10,000 3 2009 60,000 Q32008 - Q22009
SoKor Group 5,000 4 2009 70,000 Q42008 - Q32009
SoKor Group 15,000 1 2010 . Q12009 - Q42009
SoKor Group 20,000 3 2010 . Q22009 - Q12010
Thank you so much.
Here is one way to do it using Sum Over window aggregate
SELECT *,
Sum(sales)
OVER(
partition BY Company
ORDER BY [Year], [Quarter] ROWS BETWEEN 4 PRECEDING AND 1 PRECEDING)
FROM Yourtable
for Older versions
;WITH cte
AS (SELECT Row_number()OVER(partition BY Company ORDER BY [Year], [Quarter]) rn,*
FROM Yourtable a)
SELECT *
FROM cte a
CROSS apply (SELECT Sum (sales) Total_sales
FROM (SELECT TOP 4 sales
FROM cte b
WHERE a.Company = b.Company
AND b.rn < a.rn
ORDER BY [Year] DESC,
[Quarter] DESC)a) cs
#Prdp's solution is valid. However, it would show incorrect results when there are quarters missing for a given company as it will consider whatever row was available before the missing row. A way to avoid such situation is using derived tables to generate all combinations of year,quarter and company. Left joining the original table on to this result would generate 0 sales for the missing quarters. Then use the sum window function to get the sum of sales for the last 4 quarters for each row.
SELECT *
FROM
(SELECT C.COMPANY,
Y.[YEAR],
Q.[QUARTER],
T.SALES,
SUM(COALESCE(T.SALES,0)) OVER(PARTITION BY C.COMPANY
ORDER BY Y.[YEAR], Q.[QUARTER]
ROWS BETWEEN 4 PRECEDING AND 1 PRECEDING) AS PREV_4QTRS_TOTAL
FROM
(SELECT 2008 AS [YEAR]
UNION ALL SELECT 2009
UNION ALL SELECT 2010
UNION ALL SELECT 2011
UNION ALL SELECT 2012
UNION ALL SELECT 2013) Y --Add more years as required or generate them using a recursive cte or a tally table
CROSS JOIN
(SELECT 1 AS [QUARTER]
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4) Q
CROSS JOIN
(SELECT DISTINCT COMPANY
FROM T) C
LEFT JOIN T ON Y.[YEAR]=T.[YEAR]
AND Q.[QUARTER]=T.[QUARTER]
AND C.COMPANY=T.COMPANY
) X
WHERE SALES IS NOT NULL --to filter the result to include only rows from the original table
ORDER BY 1,2,3
Sample Demo