Next Formula result based on previous Formula Result SQL Server - sql

I am trying to calculate in between LAT values of records 14-23.
Here's an example:
Id LAT ODO
13 30.85629535 33.95
14 33.95
15 36.32
16 36.57
17 38.69
18 39.63
19 39.87
20 41.22
21 41.62
22 44.6
23 46.21
24 30.85633529 48.38
Assuming that we are on Record 14, then previous means the value previous to record 14. Current means the current 14 record value, Next is the Next available from the current record.
(NextAvailableLat - PrevLat) * ((CurrentODO - PrevODO) /
(NextAvailableODO - PrevODO)) + PrevLat
(30.85633529 - 30.85629535) * ((33.95 - 33.94) / (48.38 - 33.94)) + 30.85629535
When I get to record 15, my formula would need to retain the previous calculated LAT in order to calculate the new Lat
I have tried with a cursor in which I pass the ID and this works fine, however when I get to large datasets it takes a really long time. Some batches have close to 1M records which drags for hours. I was hoping for a update statement...but I can't figure out how to retain that previous calculated value.
I have an update wrapped in a cursor. The cursor passes the ID
This is the expected results:
Id LAT ODO
13 30.85629535 33.95
14 30.85629535 33.95
15 30.85630191 36.32
16 30.8563026 36.57
17 30.85630847 38.69
18 30.85631107 39.63
19 30.85631173 39.87
20 30.85631547 41.22
21 30.85631658 41.62
22 30.85632483 44.6
23 30.85632929 46.21
24 30.85633529 48.38
Script to insert data:
CREATE table dbo.Lats
(ID int
, LAT decimal(14,8)
,ODO decimal(10,2))
INSERT INTO dbo.Lats
select * from (
select 13 ID, 30.85629535 LAT, 33.95 ODO
union
select 14, null, 33.95
union
select 15, null, 36.32
union
select 16, null, 36.57
union
select 17, null, 38.69
union
select 18, null, 39.63
union
select 19, null, 39.87
union
select 20, null, 41.22
union
select 21, null, 41.62
union
select 22, null, 44.6
union
select 23, null, 46.21
union
select 24, 30.85633529 ,48.38) lats
order by ID
Below is a While loop that I used to get the results:
declare #minid int ,#maxid int, #currentID int
set #minid =13
set #maxid = 24
set #currentID = 14
WHILE #currentID <#maxid
BEGIN
DECLARE #NextLat decimal(14,8) , #PrevLat decimal(14,8) , #PrevODO decimal(10,2) , #CurrentODO decimal(10,2) , #NextODO decimal(10,2)
select #NextLat = LAT from dbo.Lats where id = #maxid
select #PrevLat = LAT from dbo.Lats where id = #currentID - 1
select #PrevODO = odo from dbo.Lats where id = #currentID - 1
select #CurrentODO = odo from dbo.Lats where id =#currentID
select #NextODO = odo from dbo.Lats where id =#maxid
update dbo.Lats
set LAT = ((#NextLat - #PrevLat) * ((#CurrentODO - #PrevODO) (#NextODO - #PrevODO)) + #PrevLat)
from dbo.Lats
where ID = #currentID
set #currentID = #currentID + 1
END

Related

return two character from function containing numbers and alphabet in SQL

I want to make function which will return two character based on the condition.
let say I have a table tbl_Dummy
CREATE TABLE [dbo].[tbl_Dummy](
[Id] [varchar](2) NOT NULL,
[Name] [varchar](30) NOT NULL
)
if Max value of Id in tbl_dummy is 01, then it returns 02, and so on, when it passes to 09, then it returns 0A til 0Z, after that it will return 10 and so on.
I have done this, but this is not working in my scenerio
create FUNCTION dbo.GenerateValue ()
RETURNS VARCHAR(250)
AS BEGIN
DECLARE #counter int = 1;
DECLARE #Work VARCHAR(2)
DECLARE #temp VARCHAR(2)
DECLARE #tempW VARCHAR(2)
declare #value int
select #Work = MAX(id) from tbl_Dummy
WHILE #counter <= DATALENGTH(#Work)
BEGIN
SELECT #temp = ASCII(SUBSTRING(#Work, #counter, 1))
SET #counter = #counter + 1
if #temp >= '48' and #temp <= '56' or #temp >= '65' and #temp <= '89'
begin
select #value = CONVERT(INT, #temp)
set #value = #temp + 1
end
else if #temp = '57'
set #value = 'A'
else if #temp = '90'
set #tempW = '0'
set #tempW += CHAR(ASCII(SUBSTRING(#Work, #counter, 1)))
END
RETURN #work
END
Instead of getting the MAX(Id) every time, you should add an IDENTITY column in your table and a computed column to compute the correct Id.
CREATE TABLE tbl_Dummy(
TempId INT IDENTITY(1, 1),
Id AS ISNULL(dbo.GenerateValue(TempId),'') PERSISTED,
Name VARCHAR(30) NOT NULL
)
This way, once you insert a row in tbl_Dummy you don't always have to compute for the latest Id. The TempId will give that for you. As for how to compute the desired Id, here is one way without looping:
CREATE FUNCTION dbo.GenerateValue (#N INT)
RETURNS CHAR(2) WITH SCHEMABINDING AS
BEGIN
RETURN
(
SELECT returnStr =
SUBSTRING(Str1, (#N/36) % LEN(Str1) + 1, 1) + --LeftMost
SUBSTRING(Str1, (#N/1) % LEN(Str1) + 1, 1) --RightMost
FROM (
SELECT '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
) d(Str1)
);
Sample Usage:
INSERT INTO dbo.tbl_Dummy(Name)
SELECT TOP 20
SomethingElse = 'Random' + CONVERT(VARCHAR(10), ROW_NUMBER() OVER(ORDER BY (SELECT NULL)))
FROM sys.all_columns ac1
SELECT * FROM dbo.tbl_DUmmy
Result:
TempId Id Name
----------- ---- ------------------------------
1 01 Random1
2 02 Random2
3 03 Random3
4 04 Random4
5 05 Random5
6 06 Random6
7 07 Random7
8 08 Random8
9 09 Random9
10 0A Random10
11 0B Random11
12 0C Random12
13 0D Random13
14 0E Random14
15 0F Random15
16 0G Random16
17 0H Random17
18 0I Random18
19 0J Random19
20 0K Random20
Reference:
An answer by Jeff Moden on a similar problem:

Fill up column with random values from different table

I have 2 tables. Table Table_View has 5 columns and 200 rows
and Table_Random has 3 columns with 10 rows
I need to poulate column Table_View.A_random with values from
Table_Random.FixValues. If the query reaches the end of row in Table_Random i.e row 10, it should start again with
values from the top row i.e row 1. until it fills up the 200 rows.
Given that all tables has primary keys.
Any Ideas?
Thanks in advance
This will work for any count of rows in destination and source tables.
The idea is to calculate count of rows in random table and then assign number rn % #c to each row in destination table. And then update based on join:
DECLARE #count INT = 21
DECLARE #i INT = 1
DECLARE #c INT = 0
DECLARE #t TABLE ( ID INT, Random INT )
DECLARE #r TABLE ( ID INT, Random INT )
INSERT INTO #r
VALUES ( 1, 10 ),
( 3, 20 ),
( 4, 30 ),
( 6, 40 ),
( 8, 50 ),
( 11, 60 ),
( 14, 70 ),
( 17, 80 ),
( 19, 90 ),
( 21, 100 )
WHILE #i <= #count
BEGIN
INSERT INTO #t
VALUES ( #i, NULL )
SET #i = #i + 1
END;
SELECT #c = COUNT(*)
FROM #r;
WITH ctet1
AS ( SELECT * , ROW_NUMBER() OVER ( ORDER BY ID ) AS rn
FROM #t
),
ctet2
AS ( SELECT * ,
CASE WHEN rn % #c = 0 THEN #c
ELSE rn % #c
END AS rnn
FROM ctet1
),
cter
AS ( SELECT * , ROW_NUMBER() OVER ( ORDER BY ID ) AS rn
FROM #r
)
UPDATE ct
SET Random = cr.Random
FROM ctet2 ct
JOIN cter cr ON cr.rn = ct.rnn
SELECT * FROM #t
Output:
ID Random
1 10
2 20
3 30
4 40
5 50
6 60
7 70
8 80
9 90
10 100
11 10
12 20
13 30
14 40
15 50
16 60
17 70
18 80
19 90
20 100
21 10
If you didn't want cycle update then no need for views, functions and needless stuff. Just update:
UPDATE #t SET Random = (SELECT TOP 1 Random FROM #r ORDER BY NEWID())
yes you can make this out.
First of all you need to create and view which'll return a single value from Random Table(Table_Random) for every call.
Create View vMyRand as
Select top 1 val from myRand order by NewID();
then create a function to return value from created view.
CREATE FUNCTION GetMyRand ()
RETURNS varchar(5)
--WITH EXECUTE AS CALLER
AS
BEGIN
Declare #RetValue varchar(5)
--#configVar =
Select #RetValue = val from vmyRand
RETURN(#retValue)
END;
Fiddle Demo Here
Full Code:
create table tab_1
(
id bigint identity(1,1),
name varchar(50),
email varchar(50)
)
insert into tab_1(name,email) values
('a','a#mail.com'), ('b','c#mail.com'),
('a1','a1#mail.com'), ('a2','a2#mail.com'),
('a3','a3#mail.com'), ('a4','a4#mail.com'),
('b1','b1#mail.com'),('b2','b2#mail.com')
create table myRand(val varchar(50))
insert into myRand values('Q1654'),('F2597'),
('Y9405'),('B6735'),('D8732'),('C4893'),('I9732'),
('L1060'),('H6720');
Create View vMyRand as
Select top 1 val from myRand order by NewID();
CREATE FUNCTION GetMyRand ()
RETURNS varchar(5)
--WITH EXECUTE AS CALLER
AS
BEGIN
Declare #RetValue varchar(5)
--#configVar =
Select #RetValue = val from vmyRand
RETURN(#retValue)
END;
Update Code:
update tab_1 set name=(select dbo.getMyRand())
Hope This'll Help You.
Thanks. :)

Recursive UNION ALL to parse string taking very long

I am trying to speed up this recursive UNION ALL as shown below, but I cannot think how to do it. Maybe a while loop but I am not sure. The movement data is stored as one long string of encoded movement data and the script recursively calls the select statement to parse/extract this data and then it is all casted.
I would really like to understand more about speeding up recursive union all's or finding another way. I don't believe indexing is a problem so this is not really a possible solution.
"RouteData" is the long string that is parsed by fixed length intervals.
Here is a sample of the encoded data:
ScenarioPID : 3
LegID :1
RoutePart : 0x0000000000000000000100000000000000000000000000
RouteData : 0x40323AAAAAAAAAAB00013FA6FFD663CCA3310000001F00403 ... (goes on)
cnt : 37
sequence : 1
StartTime : 8828
The final output data looks like this for one track.
ScenarioPID LegID sequence TrackID Offset TimeOffset Length StartTime
3 1 1 1 0 0 6300 8828
3 1 2 1 0.0449 31 6300 8828
3 1 3 1 0.8942 325 6300 8828
3 1 4 1 0.9736 356 6300 8828
3 1 5 1 1 369 6300 8828
USE nss_demo;
DECLARE #scenario1 INT;
DECLARE #DAY_START INT;
DECLARE #DAY_END INT;
DECLARE #TRAIN_TYPE VARCHAR(50);
DECLARE #TRACK_TYPE VARCHAR(50);
SET #scenario1 = 3;
SET #DAY_START = 0;
SET #DAY_END = 7;
SET #TRAIN_TYPE = 'Empty Train';
SET #TRACK_TYPE = 'East Track';
DECLARE #KM_START INT;
DECLARE #KM_END INT;
SET #KM_START = 0;
SET #KM_END = 200;
WITH movement
AS (SELECT m.scenariopid,
m.legid,
Substring(routedata, 1, 23) AS RoutePart,
Substring(routedata, 24, Len(routedata) - 23) AS RouteData,
Len(routedata) / 23 - 1 AS cnt,
1 AS sequence,
m.starttime
FROM output.movement m
WHERE scenariopid = #scenario1
AND m.starttime BETWEEN ( #DAY_START * 86400 ) AND
( #DAY_END * 86400 )
UNION ALL
SELECT scenariopid,
legid,
Substring(m1.routedata, 1, 23) AS RoutePart
,
Substring(m1.routedata, 24,
Len(m1.routedata) - 23) AS RouteData,
Len(m1.routedata) / 23 - 1 AS cnt,
sequence + 1 AS sequence,
m1.starttime
FROM movement m1
WHERE m1.cnt > 0),
casttable
AS (SELECT tt.scenariopid,
tt.legid,
tt.sequence,
tt.trackid,
tt.offset,
tt.timeoffset,
tr.[length],
tt.starttime
FROM (SELECT scenariopid,
legid,
sequence,
Cast(trackidbin AS SMALLINT) AS TrackID,
Sign(Cast(offsetbin AS BIGINT)) *
( 1.0 +
( Cast(offsetbin AS BIGINT) & 0x000FFFFFFFFFFFFF ) *
Power(Cast(2 AS FLOAT), -52) )
*
Power(Cast(2 AS FLOAT), ( Cast(offsetbin AS BIGINT) &
0x7ff0000000000000
) /
0x0010000000000000
- 1023) AS Offset,
Cast(timebin AS INT) AS TimeOffset,
starttime AS StartTime
FROM (SELECT legid,
scenariopid,
sequence,
Substring(routepart, 9, 2) AS TrackIDBin,
Substring(routepart, 11, 8) AS OffsetBin,
Substring(routepart, 19, 4) AS TimeBin,
starttime
FROM movement) t) tt
INNER JOIN input.track tr
ON tr.trackid = tt.trackid
AND tr.scenariopid = tt.scenariopid)
SELECT *
FROM casttable
ORDER BY legid,
sequence
OPTION (maxrecursion 20000)
Use a Numbers Table (zero-based assumed below) to create CTE movement like this:
WITH movement
AS (SELECT m.scenariopid,
m.legid,
Substring(routedata, n.N*23 + 1, 23) AS RoutePart,
n.N AS cnt,
-- 1 AS sequence, -- use a row_number function here instead, as per your vendor.
m.starttime
FROM output.movement m
JOIN Numbers n
on n < Len(routedata) / 23
WHERE scenariopid = #scenario1
AND m.starttime BETWEEN ( #DAY_START * 86400 ) AND
( #DAY_END * 86400 )
),
-- etc.
If you don't have a static Numbers Table, my answer here demonstrates how to create one dynamically in a CTE.

Can I do without this cursor

I have a junction table which holds dependencies between items. I am using this to programmatically create a gantt chart showing these dependencies. I have a working stored procedure now, however my company's policy is to avoid cursors where possible. I put this to the gurus out there, is it possible to do this without the cursor?
DATA:
declare #BaseTable Table
(
[IssueDependencyId] [bigint] IDENTITY(1,1) NOT NULL,
[IssueId] [bigint] NOT NULL,
[DependsOnIssueId] [bigint] NOT NULL
)
INSERT INTO #BaseTable
SELECT
48, 0
UNION ALL SELECT
49, 48
UNION ALL SELECT
50, 48
UNION ALL SELECT
51, 48
UNION ALL SELECT
55, 48
UNION ALL SELECT
56, 48
UNION ALL SELECT
52, 49
UNION ALL SELECT
52, 50
UNION ALL SELECT
52, 51
UNION ALL SELECT
53, 52
UNION ALL SELECT
57, 54
UNION ALL SELECT
54, 55
UNION ALL SELECT
57, 56
SELECT * FROM #BaseTable
STORED PROC code:
DECLARE #IssueId int, #DependsOnIssueId int, #StartPoint int, #EndPoint int
SET #StartPoint = 0
SET #EndPoint = 10
DECLARE #ResultsTable TABLE (
IssueId int not null,
DependsOnIssueId int not null,
Start_Point int,
End_Point int
)
Select IssueId, DependsOnIssueId
INTO #tmp1
FROM IssueDependency
WHERE UpperLevelIssueId = 48
ORDER BY DependsOnIssueId
declare MyCursor Cursor for (Select IssueId, DependsOnIssueId from #tmp1);
OPEN MyCursor
FETCH NEXT FROM MyCursor
INTO #IssueId, #DependsOnIssueId
WHILE ##FETCH_STATUS = 0
BEGIN
--get parent position to set start
SELECT #StartPoint = ISNULL(End_Point, 0)
FROM #ResultsTable WHERE IssueId = #DependsOnIssueId
SET #EndPoint = #StartPoint + 10
INSERT INTO #ResultsTable VALUES
(#IssueId, #DependsOnIssueId, #StartPoint, #EndPoint)
FETCH NEXT FROM MyCursor
INTO #IssueId, #DependsOnIssueId
END
Close MyCursor
DEALLOCATE MyCursor;
SELECT IssueId,
MAX(start_point) max_start_point,
MAX(end_point) max_end_point
INTO #MaxPoints
from #ResultsTable
GROUP BY IssueId
SELECT r.IssueId,DependsOnIssueId,
max_start_point start_point,
max_end_point end_point
FROM #ResultsTable r
JOIN #MaxPoints m ON m.IssueId = r.IssueId
ORDER BY r.IssueId
RESULTING DATA
IssueId DependsOnIssueId Start_Point End_Point
--------------------------------------------------------------------
48 0 0 10
49 48 10 20
50 48 10 20
51 48 10 20
52 49 20 30
52 50 20 30
52 51 20 30
53 52 30 40
54 55 20 30
55 48 10 20
56 48 10 20
57 54 30 40
57 56 30 40
Your help much appreciated!!
Generally, many of the cursor T-SQL statements can be rewrite using Recursive Common Table Expressions Recursive CTE . You can also search for some articles about how performance
is better when you are using this technique.
In you case (this is full working example), the solution looks like this:
SET NOCOUNT ON
GO
DECLARE #DataSource TABLE
(
[IssueDependencyId] BIGINT IDENTITY(1,1) NOT NULL,
[IssueId] BIGINT NOT NULL,
[DependsOnIssueId] BIGINT NOT NULL
)
INSERT INTO #DataSource ( [IssueId], [DependsOnIssueId])
VALUES (48, 0)
,(49, 48)
,(50, 48)
,(51, 48)
,(55, 48)
,(56, 48)
,(52, 49)
,(52, 50)
,(52, 51)
,(53, 52)
,(57, 54)
,(54, 55)
,(57, 56)
;WITH DataSource ([IssueId], [DependsOnIssueId], [Start_Point], [End_Point]) AS
(
SELECT AnchorMebemr.[IssueId]
,AnchorMebemr.[DependsOnIssueId]
,0
,10
FROM #DataSource AS AnchorMebemr
WHERE AnchorMebemr.[IssueId] = 48
UNION ALL
SELECT RecursiveMebemer.[IssueId]
,RecursiveMebemer.[DependsOnIssueId]
,DS.[End_Point]
,DS.[End_Point] + 10
FROM #DataSource AS RecursiveMebemer
INNER JOIN DataSource DS
ON RecursiveMebemer.[DependsOnIssueId] = DS.[IssueId]
)
SELECT DISTINCT DS.[IssueId]
,DS.[DependsOnIssueId]
,DS.[Start_Point]
,DS.[End_Point]
FROM DataSource DS
ORDER BY DS.[IssueId]
,DS.[DependsOnIssueId]
SET NOCOUNT OFF
GO
The screenshot below displays the output after the execution of the T-SQL statements above:
Note: I have noticed that in your last row you might have syntax error (as I have understand the logic):
Anyway, If I have misunderstood something, I am sure you have got the idea.
I have not tested this. I am using a autoincrement column to loop through the temp1 table. Here it goes:
DECLARE #tmp1 table
(
_ID int identity (1,1) , -- will be used for looping
IssueId int not null,
DependsOnIssueId int not null
)
DECLARE #i as int
DECLARE #max as int
INSERT INTO #tmp1 (IssueId, DependsOnIssueId )
Select IssueId, DependsOnIssueId
FROM IssueDependency
WHERE UpperLevelIssueId = 48
ORDER BY DependsOnIssueId
SELECT #i = 1, #max = MAX(_ID) FROM #tmp1
WHILE #i <= #max
BEGIN
SELECT #IssueId = IssueId, #DependsOnIssueId = DependsOnIssueId
FROM #tmp1 WHERE _ID = #i
--get parent position to set start
SELECT #StartPoint = ISNULL(End_Point, 0)
FROM #ResultsTable WHERE IssueId = #DependsOnIssueId
SET #EndPoint = #StartPoint + 10
INSERT INTO #ResultsTable VALUES
(#IssueId, #DependsOnIssueId, #StartPoint, #EndPoint)
SET #i = #i + 1
END

get specific rows of table given a rule SQL Server 2008

I have a table like:
ID NAME VAL
----------------------
1 a1*a1 90052
2 a1*a2 236
3 a1*a3 56
4 a1*a4 6072
5 a1*a5 1004
6 a2*a2 4576
7 a2*a3 724
8 a2*a4 230
9 a2*a5 679
10 a3*a3 5
11 a3*a4 644
12 a3*a5 23423
13 a4*a4 42354
14 a4*a5 10199
15 a5*a5 10279
Given a number given S = 5, I want to query
the rows wth id: 1,6,10,13,15
they are a1*a1,a2*a2,a3*a3,a4*a4 and a5*a5
I would like something like:
INSERT #NEW_TABLE (ID,NAME,Value) (
SELECT ordinal, NAME, VAL FROM myTable where id = 1,6,10,13,15)
to get
ID NAME VAL
----------------------
1 a1*a1 90052
2 a2*a2 4576
3 a3*a3 5
4 a4*a4 42354
5 a5*a5 10279
Is there a way to do this for any given S, Maybe wth dynamic sql?
I was getting the formula and I got this:
S=5
ID formula
1 1
6 1+S
10 1+S+ (S-1)
13 1+S+ (S-1) + (S-2)
15 1+S+ (S-1) + (S-2) + (S-3)
Is there a way to do this inside a case or a while loop?
This worked in testing.
You can just inner join on #Tab to limit your results. You probably also want to add some traps for values below 3, which I haven't done.
The basic process is
Declare your #s value
Insert the first two rows since they will always be the same
In a loop, insert one row at a time with an incrementing difference
Loop exits once it has run #s-2 times
Try:
DECLARE #Tab Table (id INT)
DECLARE #S int = 5,
#ct int
DECLARE #cur int = (1 + #S)
INSERT INTO #Tab SELECT 1
INSERT INTO #Tab SELECT (1 + #S)
SET #ct = 1
WHILE #ct <= #S - 2
BEGIN
SET #cur = #cur + (#S - #ct)
INSERT INTO #Tab SELECT #cur
SET #ct = #ct + 1
END
SELECT * FROM #Tab
ORDER BY id
To use this in your query, you can do either:
SELECT ordinal, NAME, VAL
FROM myTable
WHERE id IN (SELECT id FROM #Tab)
-- OR
SELECT ordinal, NAME, VAL
FROM myTable t
INNER JOIN #tab t2
ON t2.id = t.id