Adding each row with aprevious row in sql query - sql

Lets say I have a table with a values below:
Date sales
===== =====
Jan 100
Feb 150
Mar 500
and so on
How can I query this table with the results below:
Date Sales Total
==== ===== ======
Jan 100 100
Feb 150 250 (Jan + Feb)
Mar 500 750 (Jan + Feb + mar)
I know it can be done in SP looping through but is there a simple query?
your help is appreciated.
Thanks,
J

A windowed SUM should work on several DBMS. A SQL Server example is below (please let us know which one you are using otherwise):
DECLARE #T TABLE ([Date] DATE, [Sales] INT)
INSERT #T VALUES ('1/1/2015', 100), ('2/1/2015', 150), ('3/1/2015', 500)
SELECT
[Date],
[Sales],
SUM([Sales]) OVER (ORDER BY [Date]) AS [Total]
FROM #T
ORDER BY
[Date]
This generates the following output:
Date Sales Total
---------- ----------- -----------
2015-01-01 100 100
2015-02-01 150 250
2015-03-01 500 750
Since SQL Server 2008 doesn't support the ORDER BY in a windowed aggregate (only since 2012), here's a method to do same thing. It's very inefficient - there's just not a very efficient way to do this otherwise I've seen unfortunately.
;WITH CTE AS (
SELECT
ROW_NUMBER() OVER (ORDER BY [Date]) AS RowId,
[Date],
[Sales]
FROM #T
)
SELECT
A.[Date],
A.[Sales],
SUM(B.[Sales]) AS [Total]
FROM CTE A
INNER JOIN CTE B
ON B.RowId <= A.RowId
GROUP BY
A.[Date],
A.[Sales]
ORDER BY
A.[Date]

Related

Return next value in Sybase based on condition

I have a table like this:
date ticker price
01/01/17 APPL 700
01/01/17 SNAP 15
01/02/17 APPL 750
01/02/17 SNAP 13
I'd want to retrieve the next price for that ticker as an additional column, like so:
date ticker price next_price
01/01/17 APPL 700 750
01/01/17 SNAP 15 13
01/02/17 APPL 750 NULL
01/02/17 SNAP 13 NULL
I think in most databases you'd be able to do something like this:
SELECT date, ticker, price, RANK() OVER (PARTITION BY ticker
ORDER BY date ASC) AS RANK
from table_name
and then do something with the rank to find the next_price. Unfortunately Sybase ASE is sadly limited and doesn't support RANK().
Any ideas on what to use instead?
Assumptions:
each unique ticker value has a max of 1 record for any given date
next_price is for the next day where 'next' could be defined as +1 day, +2 days, +1 week, +1 month, etc
Off the top of my head ... a bit of a convoluted correlated sub-query to find next_price ...
Setup table and data:
create table mytab
([date] date
,ticker varchar(10)
,price int
)
go
insert mytab values ('1/1/2017','APPL',700)
insert mytab values ('1/1/2017','SNAP',15)
insert mytab values ('1/2/2017','APPL',750)
insert mytab values ('1/2/2017','SNAP',13)
insert mytab values ('1/5/2017','APPL',800)
insert mytab values ('1/7/2017','SNAP',23)
go
One possible query:
select t1.[date],
t1.ticker,
t1.price,
(select price
from mytab t2
where t2.ticker = t1.ticker
and t2.[date] = (-- find the 'next' available day for t1.ticker
select min([date])
from mytab t3
where t3.ticker = t1.ticker
and t3.[date] > t1.[date]
)
) as next_price
from mytab t1
order by 1,2
go
date ticker price next_price
---------------- ---------- ----------- -----------
Jan 1 2017 APPL 700 750
Jan 1 2017 SNAP 15 13
Jan 2 2017 APPL 750 800
Jan 2 2017 SNAP 13 23
Jan 5 2017 APPL 800 NULL
Jan 7 2017 SNAP 23 NULL
Tested on ASE 15.7 SP138
You would not use rank() for this. You would use lead().
You can use a correlated subquery:
select t.*,
(select top 1 t2.price
from table_name t2
where t2.ticker = t.ticker and t2.date > t.date
order by t2.date asc
) as next_price
from table_name t;
If you know the date is the next calendar date, then you could use a left join instead -- that would be more efficient.

in sql how to arrange rows of data into colum using Pivot

Here i have a simple table i want to display all rows respected that year display in single column
Year Month Amt
1999 Jan 520
1999 Feb 100
199 Mar 200
2000 Jan 500
2000 Feb 200
I want to display these table as
Year Jan Feb Mar
1999 520 100 200
2000 500 200 null
I had Written query as invoice its my table name
select
[Jan] as January,
[Feb] as Feburary,
[March] as Feburary,
from(
select Year,month,amount from invoice)x
PIVOT(
sum(amount)
for month in([jan],[Feb],[March])
)p
Please Try This Query
create table #Invoice
(
ID int identity(1,1),
Year varchar(4),
Month varchar(3),
Amount int
)
insert into #Invoice (Year, Month, Amount) values ('1999','Jan',520),('1999','Feb',100),('1999','Mar',200),
('2000','Jan',500),('2000','Feb',200)
select Year, [Jan], [Feb],[Mar],[Grand Total]
from (
select Year, Month, Amount
from #Invoice
Union all
select Year, 'Grand Total', SUM(Amount)
from #Invoice
group by year
)dd
pivot (
sum(Amount) for Month in ([Jan], [Feb],[Mar],[Grand Total])
) piv
drop table #Invoice
You can achieve it using CTE. You can't GROUP BY YEAR within the PIVOT table operator, the PIVOT operator infers the grouped columns automatically. This seems to work
WITH Pivoted
AS
(
SELECT *
FROM table1
PIVOT
(
sum([Amt]) FOR [month] IN ( [jan],[Feb],[Mar])
) AS p
)
SELECT
Year,
sum([Jan]) as January,
sum([Feb]) as Feburary,
sum([Mar]) as March
FROM Pivoted
GROUP BY Year;
REXTESTER DEMO

Comparing data from two rows in a same sql table

I am trying to find out differences between two rows in a same table. Having trouble to find right query. For example, I have
Year Item Qty Amount
------------------------------
2014 Shoes 500 2500
2014 Ties 300 900
2014 Pants 200 4000
2015 Shoes 600 3000
2015 Ties 200 600
I am trying to find out what was the increased (or decreased) from previous year to this year. I will always have only two years to compare. The query result should look like following:
Items Qty Diff Amount Diff
------------------------------
Shoes 100 500
Ties (-100) (-300)
Pants Null Null
What should be the query look like?
If you want to include everything, then you can use FULL OUTER JOIN, if just the one with the earlier year, LEFT OUTER JOIN, if you want the one with both earlier and subsequent year, then INNER JOIN.
SELECT
T1.Item
, (T2.QTY-T1.QTY) AS [QTY Diff]
, (T2.Amount - T1.Amount) AS [Amount Diff]
FROM
<<Table>> T1
LEFT OUTER JOIN <<Table>> T2
ON T1.Item=T2.Item
AND T1.YEAR=(T2.YEAR-1);
1. Use LAG or LEAD
WITH tb(Year,Item,Qty,Amount) AS (
SELECT 2014,'Shoes',500,2500 UNION
SELECT 2014,'Ties',300,900 UNION
SELECT 2014,'Pants',200,4000 UNION
SELECT 2015,'Shoes',600,3000 UNION
SELECT 2015,'Ties',200,600
)
SELECT *,Qty-LAG(qty)OVER(PARTITION BY Item ORDER BY year) AS QtyDiff ,Amount-LAG(Amount)OVER(PARTITION BY Item ORDER BY year) AS AmountDiff
FROM tb
Year Item Qty Amount QtyDiff AmountDiff
----------- ----- ----------- ----------- ----------- -----------
2014 Pants 200 4000 NULL NULL
2014 Shoes 500 2500 NULL NULL
2015 Shoes 600 3000 100 500
2014 Ties 300 900 NULL NULL
2015 Ties 200 600 -100 -300
2.Cross or Outer Apply
WITH tb(Year,Item,Qty,Amount) AS (
SELECT 2014,'Shoes',500,2500 UNION
SELECT 2014,'Ties',300,900 UNION
SELECT 2014,'Pants',200,4000 UNION
SELECT 2015,'Shoes',600,3000 UNION
SELECT 2015,'Ties',200,600
)
SELECT t1.Year,t1.Item,t1.Qty- t2.qty AS DiffQty,t1.Amount-t2.Amount AS DiffAmount
FROM tb AS t1
OUTER APPLY (SELECT TOP 1 tt.qty,tt.Amount FROM tb AS tt WHERE tt.Year<t1.Year AND t1.Item=tt.Item ORDER BY tt.Year desc) AS t2
ORDER BY t1.Item,t1.Year
Using the lag function is the best approach to this.
SELECT [Year], [Item], [Qty], [Amount],
[Qty] - LAG([Qty]) OVER (PARTITION BY [Item] ORDER BY [Year]) [QtyDiff],
[Amount] - LAG([Amount]) OVER (PARTITION BY [Item] ORDER BY [Year]) [AmountDiff]
FROM [ItemTable] it
order BY [Year] DESC, [Item];
Hope this helps.
Here is the required query:
SET #YEAR1 = '2014';
SET #YEAR2 = '2015';
SELECT
Item,
if(count(*)>1,sum(if(Year=#YEAR2,Qty,-Qty)),NULL) as 'Qty Diff',
if(count(*)>1,sum(if(Year=#YEAR2,Amount,-Amount)),NULL) as 'Amount Diff'
FROM
table
WHERE
Year IN (#YEAR1,#YEAR2)
group by Item;

Syntax to get sum(sales) group by brand but different date

My data is like so
item date country sales
----------------------------------------
motorola 2015-01-01 US 10
motorola 2015-01-01 UK 20
motorola 2015-01-02 US 40
motorola 2015-01-02 UK 80
motorola 2015-01-03 US 120
motorola 2015-01-03 UK 150
motorola 2015-01-04 US 170
motorola 2015-01-04 US 180
I want to get the daily sales delta of motorola from 2 jan 2015 until 4 jan 2015.
So for example
total sales for 1 jan 2015 is 10 (US) + 20(UK) = 30
total sales for 2 jan 2015 is 120 so daily sales delta (sales on date minus D-1) is 90
total sales for 3 jan 2015 is 270 so daily delta is 150
total sales for 4 jan 2015 is 350 so daily delta is 80
I'm expecting the result tuple :
date dailyDelta
2015-01-02 90
2015-01-03 150
2015-01-04 80
What is the syntax to get this? I'm using SQL Server 2012.
Thanks
This is it, the query logic is as simple as it gets, the performance better than inner joins:
select date, sum(sales) - coalesce(lag(sum(sales), 1) over (order by date), 0)
from my_sales
group by date
order by date
Use window function lag. Play with it: http://sqlfiddle.com/#!6/bebab/8 and read about it: https://msdn.microsoft.com/en-us/library/hh231256.aspx
briefly, lag(sum(sales), 1) over (order by date) means "get sum(sales) column of previous record of this query, ordered by date", coalesce(XXX, 0) means "when XXX is null, let's pretend it was a zero"
I don't really see much of a way around a self join. Here is how it would work:
select a.date
, a.item
, sum(a.sales) - sum(b.sales) as DailyDelta
from table a
join table b on a.product = b.product
and b.date = dateadd(day, -1, a.date)
group by a.date
, a.item
Not great performance wise but it will get the job done.
The following query is based on MySQL, but you can tweak it to make it work for SQL server. I hope SQL Server will also support the same
select o.date, t.tsales - sum(sales) delta from test o, (select date, sum(sales) tsales from test group by date) t
where o.date = t.date -1 group by o.date
For the above query I got the following result
"date" "delta"
"2015-01-01" "90"
"2015-01-02" "150"
"2015-01-03" "80"
use a self join
declare #t table (item varchar(10), [date] date,country char(2), sales int)
insert into #t (item, [date],country, sales) values
('motorola','2015-01-01','US',10),
('motorola','2015-01-01','UK',20),
('motorola','2015-01-02','US',40),
('motorola','2015-01-02','UK',80),
('motorola','2015-01-03','US',120),
('motorola','2015-01-03','UK',150),
('motorola','2015-01-04','US',170),
('motorola','2015-01-04','US',180)
;with a as (select row_number() over (order by [date]) r,[date],sum(sales) n from #t group by [date])
select a.[date],a.n-isnull(a1.n,0) dailyDelta from a join a a1 on a.r =a1.r+1
Try this..
SELECT date,
( sum(sales) - LAG(sum(sales),1,0) over (order by sales) ) as dailyDelta
FROM Table
group by date
order by date;

SQL query to calculate days worked per Month

Im stuck on a SQL query. Im using SQL Server.
Given a table that contains Jobs with a start and end date. These jobs can span days or months. I need to get the total combined number of days worked each month for all jobs that intersected those months.
Jobs
-----------------------------------
JobId | Start | End | DayRate |
-----------------------------------
1 | 1.1.13 | 2.2.13 | 2500 |
2 | 5.1.13 | 5.2.13 | 2000 |
3 | 3.3.13 | 2.4.13 | 3000 |
The results i need are:
Month | Days
--------------
Jan | 57
Feb | 7
Mar | 28
Apr | 2
Any idea how i would right such a query ?
I would also like to work out the SUM for each month based on multiplying the dayrate by number of days worked for each job, how would i add this to the results ?
Thanks
You can use recursive CTE to extract all days from start to end for each JobID and then just group by month (and year I guess).
;WITH CTE_TotalDays AS
(
SELECT [Start] AS DT, JobID FROM dbo.Jobs
UNION ALL
SELECT DATEADD(DD,1,c.DT), c.JobID FROM CTE_TotalDays c
WHERE c.DT < (SELECT [End] FROM Jobs j2 WHERE j2.JobId = c.JobID)
)
SELECT
MONTH(DT) AS [Month]
,YEAR(DT) AS [Year]
,COUNT(*) AS [Days]
FROM CTE_TotalDays
GROUP BY MONTH(DT),YEAR(DT)
OPTION (MAXRECURSION 0)
SQLFiddle DEMO
PS: There are 58 days in Jan in your example and not 57 ;)
You can do it using following approach:
/* Your table with periods */
declare #table table(JobId int, Start date, [End] date, DayRate money)
INSERT INTO #table (JobId , Start, [End], DayRate)
VALUES
(1, '20130101','20130202', 2500),
(2,'20130105','20130205', 2000),
(3,'20130303','20130402' , 3000 )
/* create table where stored all possible dates
if this code are supposed to be executed often you can create
table with dates ones to avoid overhead of filling it */
declare #dates table(d date)
declare #d date='20000101'
WHILE #d<'20500101'
BEGIN
INSERT INTO #dates (d) VALUES (#d)
SET #d=DATEADD(DAY,1,#d)
END;
/* and at last get desired output */
SELECT YEAR(d.d) [YEAR], DATENAME(month,d.d) [MONTH], COUNT(*) [Days]
FROM #dates d
CROSS JOIN #table t
WHERE d.d BETWEEN t.Start AND t.[End]
GROUP BY YEAR(d.d), DATENAME(month,d.d)
This only have 1 recursive call instead of 1 for each row. I imagine this will perform better than the chosen answer when you have large amount of data.
declare #t table(JobId int, Start date, [End] date, DayRate int)
insert #t values
(1,'2013-01-01','2013-02-02', 2500),(2,'2013-01-05','2013-02-05', 2000),(3,'2013-03-03', '2013-04-02',3000)
;WITH a AS
(
SELECT min(Start) s, max([End]) e
FROM #t
), b AS
(
SELECT s, e from a
UNION ALL
SELECT dateadd(day, 1, s), e
FROM b WHERE s <> e
)
SELECT
MONTH(b.s) AS [Month]
,YEAR(b.s) AS [Year]
,COUNT(*) AS [Days]
,SUM(DayRate) MonthDayRate
FROM b
join #t t
on b.s between t.Start and t.[End]
GROUP BY MONTH(b.s),YEAR(b.s)
OPTION (MAXRECURSION 0)
Result:
Month Year Days MonthDayRate
1 2013 58 131500
2 2013 7 15000
3 2013 29 87000
4 2013 2 6000