I have the rows below, and i want to access prior row and divide its value by current row. For every row, i need to calculate the Vi value, this Vi value is equal to Vi-1/Vi which means that:
Given the table
Table T
id value out
1 100
2 200
3 10
4 50
I want to generate these values
V1 = 100
V2= 100/200 = 0.5
V3 = 0.5/10 = 0.05
V4 = 0.05/50 = 0.001
So at the end i want the following output:
id value out
1 100 100
2 200 0.5
3 10 0.05
4 50 0.001
I tried using the aggregate function SUM with OVER(), but i do not know how to solve this problem as i need to divide and not sum the value
SELECT id, value, SUM(value) OVER(ORDER BY id ROWS BETWEEN
1 PRECEDING AND 1 PRECEDING ) / value as out
FROM T
Sample data:
CREATE TABLE t(
id INT,
value INT
);
INSERT INTO t VALUES
(1, 100), (2, 200), (3, 10), (4, 50);
Unfortunately, SQL do not have Product, but it should be simple to use cte. The performance should be not bad if id was indexed
DECLARE #T table (id int identity(1,1) primary key, value int)
INSERT #T VALUES (100), (200), (10), (50)
;WITH cte AS
(
SELECT id, value, CAST(value AS decimal(20,4)) AS out FROM #T WHERE id = 1
UNION ALL SELECT T.id, T.value, CAST(cte.out / T.value AS decimal(20,4)) FROM cte INNER JOIN #T T ON cte.id = T.id - 1
)
SELECT * FROM cte
Related
I have a question, I have a sample data shown below: my question is I want to calculate the difference between second row starting number (150) to first row ending number (100).
Can any one please help me? This answer will reduce some manual work what I'm doing daily.
Thanks
Sample data
create table MyTable
(
Model nvarchar(5),
StartingNo int,
EndingNo int
);
insert into MyTable (Model, StartingNo, EndingNo) values
('X', 1, 100),
('X', 150, 200),
('Y', 10, 30),
('Y', 1, 5);
Solution 1
This solution uses an outer apply to fetch the previous row.
select t1.*,
t3.EndingNo as PreviousEndingNo,
t1.StartingNo - t3.EndingNo as Diff
from MyTable t1
outer apply ( select top 1 t2.EndingNo -- first row...
from MyTable t2
where t2.Model = t1.Model -- ... with same model
and t2.StartingNo < t1.StartingNo -- ... that comes before current row
order by t2.StartingNo desc ) t3 -- ... when sorting on start number
order by t1.Model, t1.StartingNo;
Solution 2
This solution uses the lag function to fetch the previous row.
select t1.*,
lag(t1.EndingNo) over(partition by t1.Model order by t1.StartingNo) as PreviousEndingNo,
t1.StartingNo - lag(t1.EndingNo) over(partition by t1.Model order by t1.StartingNo) as Diff
from MyTable t1
order by t1.Model, t1.StartingNo;
Result
Model StartingNo EndingNo PreviousEndingNo Diff
----- ----------- ----------- ---------------- -----------
X 1 100 NULL NULL
X 150 200 100 50
Y 1 5 NULL NULL
Y 10 30 5 5
Fiddle to see everything in action.
I have an issue where I have to calculate a column using a formula that uses the value from the calculation done in the previous row.
I have tried the lag function but cannot get past the 2nd row. After that all my values are null, since that column originally starts as null. I feel like I am missing something.
I need to calculate a new column, using the formula:
MovingRate = MonthlyRate + (0.7 * MovingRatePrevious)
... where the MovingRatePrevious is the MovingRate of the prior row. For month 1, I have the value so I do not need to re-calculate that but I need that value to be able to calculate the subsequent rows. I need to partition by Type.
This is my original dataset:
Month Type MonthyRate MovingRate
--------------------------------------
1 Blue 0.400 0.330
2 Blue 0.300
3 Blue 0.700
4 Blue 0.900
Desired results in MovingRate column:
Month Type MonthyRate MovingRate
---------------------------------------
1 Blue 0.400 0.330
2 Blue 0.300 0.531
3 Blue 0.700 1.072
4 Blue 0.900 1.650
You can calculate it using recursive CTE. Below is a generalized version for your data:
DECLARE #t TABLE (Month INT, Type VARCHAR(100), MonthlyRate DECIMAL(18, 3));
INSERT INTO #t VALUES
(1, 'Blue', 0.400),
(2, 'Blue', 0.300),
(3, 'Blue', 0.700),
(4, 'Blue', 0.900);
WITH cte1 AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Type ORDER BY Month) AS rn
FROM #t
), rcte AS (
SELECT *, CAST(0.330 AS DECIMAL(18, 3)) AS MovingRate
FROM cte1 AS base
WHERE rn = 1
UNION ALL
SELECT curr.*, CAST(curr.MonthlyRate + 0.7 * prev.MovingRate AS DECIMAL(18, 3))
FROM cte1 AS curr
JOIN rcte AS prev ON curr.Type = prev.type AND curr.rn = prev.rn + 1
)
SELECT *
FROM rcte
This is not a homework question.
I'm trying to take the count of t-shirts in an order and see which price range the shirts fall into, depending on how many have been ordered.
My initial thought (I am brand new at this) was to ask another table if count > 1st price range's maximum, and if so, keep looking until it's not.
printing_range_max printing_price_by_range
15 4
24 3
33 2
So for example here, if the order count is 30 shirts they would be $2 each.
When I'm looking into how to do that, it looks like most people are using BETWEEN or IF and hard-coding the ranges instead of looking in another table. I imagine in a business setting it's best to be able to leave the range in its own table so it can be changed more easily. Is there a good/built-in way to do this or should I just write it in with a BETWEEN command or IF statements?
EDIT:
SQL Server 2014
Let's say we have this table:
DECLARE #priceRanges TABLE(printing_range_max tinyint, printing_price_by_range tinyint);
INSERT #priceRanges VALUES (15, 4), (24, 3), (33, 2);
You can create a table with ranges that represent the correct price. Below is how you would do this in pre-2012 and post-2012 systems:
DECLARE #priceRanges TABLE(printing_range_max tinyint, printing_price_by_range tinyint);
INSERT #priceRanges VALUES (15, 4), (24, 3), (33, 2);
-- post-2012 using LAG
WITH pricerange AS
(
SELECT
printing_range_min = LAG(printing_range_max, 1, 0) OVER (ORDER BY printing_range_max),
printing_range_max,
printing_price_by_range
FROM #priceRanges
)
SELECT * FROM pricerange;
-- pre-2012 using ROW_NUMBER and a self-join
WITH prices AS
(
SELECT
rn = ROW_NUMBER() OVER (ORDER BY printing_range_max),
printing_range_max,
printing_price_by_range
FROM #priceRanges
),
pricerange As
(
SELECT
printing_range_min = ISNULL(p2.printing_range_max, 0),
printing_range_max = p1.printing_range_max,
p1.printing_price_by_range
FROM prices p1
LEFT JOIN prices p2 ON p1.rn = p2.rn+1
)
SELECT * FROM pricerange;
Both queries return:
printing_range_min printing_range_max printing_price_by_range
------------------ ------------------ -----------------------
0 15 4
15 24 3
24 33 2
Now that you have that you can use BETWEEN for your join. Here's the full solution:
-- Sample data
DECLARE #priceRanges TABLE
(
printing_range_max tinyint,
printing_price_by_range tinyint
-- if you're on 2014+
,INDEX ix_xxx NONCLUSTERED(printing_range_max, printing_price_by_range)
-- note: second column should be an INCLUDE but not supported in table variables
);
DECLARE #orders TABLE
(
orderid int identity,
ordercount int
-- if you're on 2014+
,INDEX ix_xxy NONCLUSTERED(orderid, ordercount)
-- note: second column should be an INCLUDE but not supported in table variables
);
INSERT #priceRanges VALUES (15, 4), (24, 3), (33, 2);
INSERT #orders(ordercount) VALUES (10), (20), (25), (30);
-- Solution:
WITH pricerange AS
(
SELECT
printing_range_min = LAG(printing_range_max, 1, 0) OVER (ORDER BY printing_range_max),
printing_range_max,
printing_price_by_range
FROM #priceRanges
)
SELECT
o.orderid,
o.ordercount,
--p.printing_range_min,
--p.printing_range_max
p.printing_price_by_range
FROM pricerange p
JOIN #orders o ON o.ordercount BETWEEN printing_range_min AND printing_range_max
Results:
orderid ordercount printing_price_by_range
----------- ----------- -----------------------
1 10 4
2 20 3
3 25 2
4 30 2
Now that we have that we can
I'm designing table which will contain properties of some objects which will change over time.
CREATE TABLE [dbo].[ObjectProperties]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY,
[ObjectType] SMALLINT NOT NULL,
[Width] SMALLINT NOT NULL,
[Height] SMALLINT NOT NULL,
[Weight] SMALLINT NOT NULL
)
Let's say I have this ObjectTypes:
1 = Chair
2 = Table
And Data for this table:
INSERT INTO [dbo].[ObjectProperties] ([Id], [ObjectType], [Width], [Height], [Weight]) VALUES (1, 1, 50, 50, 1000)
INSERT INTO [dbo].[ObjectProperties] ([Id], [ObjectType], [Width], [Height], [Weight]) VALUES (2, 2, 80, 40, 500)
INSERT INTO [dbo].[ObjectProperties] ([Id], [ObjectType], [Width], [Height], [Weight]) VALUES (3, 1, 50, 50, 2000)
So, as you can see I had Chair object which Weight was 1000 then I changed weight to 2000. And I'm storing something like modification history of objects properties.
Now I want to select newest data from this table for each object. I know how to select newest data for each object one by one:
SELECT TOP 1 * FROM [ObjectProperties] WHERE ObjectType = 1 ORDER BY Id DESC
But what if I want to select few objects with one query? Like
SELECT ... * FROM [ObjectProperties] WHERE ObjectType IN (1, 2) ...
And receive rows with ids 2 and 3 (because 3 has newer properties for Chair than 1)
You can use a CTE with ROW_NUMBER ranking function:
WITH CTE AS(
SELECT *,
RN=ROW_NUMBER()OVER(PARTITION BY ObjectType ORDER BY ID DESC)
FROM [ObjectProperties] op
)
SELECT * FROM CTE WHERE RN = 1
AND ObjectType IN (1, 2)
Demo
The ROW_NUMBER returns one row for every ObjectType-group order by ID DESC(so the record with the highest ID) .If you want to filter by certain ID's you just have to apply the appropriate WHERE clause, either in the CTE or in the outer SELECT.
Ranking Functions
A simple (admittedly crude) way is as follows:
select * from ObjectProperties where id in
(select max(id) from ObjectProperties group by objecttype)
This gives:
Id ObjectType Width Height Weight
----------- ---------- ------ ------ ------
2 2 80 40 500
3 1 50 50 2000
I have a problem with a query.
This is the data (order by Timestamp):
Data
ID Value Timestamp
1 0 2001-1-1
2 0 2002-1-1
3 1 2003-1-1
4 1 2004-1-1
5 0 2005-1-1
6 2 2006-1-1
7 2 2007-1-1
8 2 2008-1-1
I need to extract distinct values and the first occurance of the date. The exception here is that I need to group them only if not interrupted with a new value in that timeframe.
So the data I need is:
ID Value Timestamp
1 0 2001-1-1
3 1 2003-1-1
5 0 2005-1-1
6 2 2006-1-1
I've made this work by a complicated query, but am sure there is an easier way to do it, just cant think of it. Could anyone help?
This is what I started with - probably could work with that. This is a query that should locate when a value is changed.
> SELECT * FROM Data d1 join Data d2 ON d1.Timestamp < d2.Timestamp and
> d1.Value <> d2.Value
It probably could be done with a good use of row_number clause but cant manage it.
Sample data:
declare #T table (ID int, Value int, Timestamp date)
insert into #T(ID, Value, Timestamp) values
(1, 0, '20010101'),
(2, 0, '20020101'),
(3, 1, '20030101'),
(4, 1, '20040101'),
(5, 0, '20050101'),
(6, 2, '20060101'),
(7, 2, '20070101'),
(8, 2, '20080101')
Query:
;With OrderedValues as (
select *,ROW_NUMBER() OVER (ORDER By TimeStamp) as rn --TODO - specific columns better than *
from #T
), Firsts as (
select
ov1.* --TODO - specific columns better than *
from
OrderedValues ov1
left join
OrderedValues ov2
on
ov1.Value = ov2.Value and
ov1.rn = ov2.rn + 1
where
ov2.ID is null
)
select * --TODO - specific columns better than *
from Firsts
I didn't rely on the ID values being sequential and without gaps. If that's the situation, you can omit OrderedValues (using the table and ID in place of OrderedValues and rn). The second query simply finds rows where there isn't an immediate preceding row with the same Value.
Result:
ID Value Timestamp rn
----------- ----------- ---------- --------------------
1 0 2001-01-01 1
3 1 2003-01-01 3
5 0 2005-01-01 5
6 2 2006-01-01 6
You can order by rn if you need the results in this specific order.