Moving calculations using SQL - sql

I need to calculate a new column using moving calculations.
For example, I have a table:
A
B
10
15
11
14
12
13
I need to calculate new column where the 1st value is calculated like 5000/10*15, the 2nd value is (5000 / 10 * 15) / 11 * 14, the 3rd one is ((5000 / 10 * 15) / 11 * 14) / 12 * 13 and so on. Where 5000 is a random value and in the future I will use it like a parameter in a stored procedure.
I know, that in Excel for example we can reffer to the previous calculated cell. How can it be calculated using SQL?
Thank you!

create table #test (A int,B int)
insert into #test values(10,15),(11,14),(12,13)
declare #seed int=5000;
;with temp as (
select A,B,row_number() over(order by a) rn from #test
),
cte as
(
select #seed/A*B calculated,rn from temp where rn=1
union all
select c. calculated/t.A*t.B,t.rn from temp t
join cte c on t.rn=c.rn+1
)
select * from cte

There is a warning in the docs that reads:
If there are multiple assignment clauses in a single SELECT statement,
SQL Server does not guarantee the order of evaluation of the
expressions. Note that effects are only visible if there are
references among the assignments.
It means there is no guarantee that it will evaluate the expression left-to-right. For this code:
declare #a int, #b int;
select #a = 2, #b = #a * 3;
select #a, #b;
The result could be 2, 6 (#a = ... evaluated first) or 2, NULL (#b = ... evaluated first).

Related

select records that one column are numbers close to 10

I have a table with 3 columns.
one of them is [Code]. I have many records on this table.
I want to select records that their [Code] are numbers close to 10 regularly
for example if select records that has [Code]=9 then select records that has [Code] = 8 etc...
This is what I implement based on your though.
If you wish near record or record-id, not value, then you can change only condition a.data to a.rid.
declare #t table (data int)
insert into #t values(1), (2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(50),(51),(52)
declare #value int = 11 , #getDatToValue int = 2
select * from
(
select * , ROW_NUMBER( ) over(order by data) rid
from #t
)
a
where
a.data between (#value - #getDatToValue) and (#value + #getDatToValue)

How to select records testing bits in BINARY column?

I have 2 tables: one of them contains column binary(128), where every bit is some flag. Also I have other table contains list of bit positions which need to check in query.
This is example how I do the query for one bit.
How to do it universally, i.e. to select records testing bits in positions from the second table?
Should it be a function? what?
DECLARE #nByteNum integer
DECLARE #nBitNumInByte integer
DECLARE #nMask integer
DECLARE #nBigBitNum integer
declare #t table(id int not null identity, id1 int, banner binary(128))
declare #bitpositions table(id int not null identity, position int)
insert into #bitpositions(position) values(8)
insert into #bitpositions(position) values(24)
insert into #bitpositions(position) values(30)
insert into #t(id1, banner)
select 1, 0x0
union all
select 1, 0x000100FF
union all
select 1, 0x010200FF
union all
select 10, 0x010208
union all
select 10, 0x000100
union all
select 10, 0x040000
select * from #t
-- This is for one bit
SET #nBigBitNum= 24
SET #nByteNum= #nBigBitNum/8
SET #nBitNumInByte= #nBigBitNum % 8 -- 0,1...6,7
SET #nMask = POWER(2, #nBitNumInByte ) -- 128,64,... 2,1
SET #nByteNum= #nByteNum +1
select * from #t where SUBSTRING(banner, #nByteNum,1)&#nMask=#nMask
Bitwise operations in SQL Server do not work on BINARY data in the way that you are expecting. They work on integer datatypes only as noted on the MSDN page for & (Bitwise AND).
The general syntax for checking bitmasked values in a query is: WHERE {Column} & {BitMaskedValue} = {BitMaskedValue}.
Look at the following examples using your desired scenario of trying to find records that have bit positions 8 and 16 turned on.
-- 24 = 16 + 8
SELECT 24 & 16 -- 16
SELECT 24 & 8 -- 8
SELECT 24 & 17 -- 16
SELECT 24 & 32 -- 0
SELECT 24 & 26 -- 24

Find missing numerical values in range [duplicate]

This question already has answers here:
How can we find gaps in sequential numbering in MySQL?
(16 answers)
Closed 8 years ago.
I've read several articles that one of the mistakes a common programmer does is not using SQL's potential and since then I started searching for replacing parts of my code with SQLish solutions rather than fetching data and processing with a programming language, although I'm a real rookie with SQL.
Say I have a table randomly populated with values from 0 to 10 and I want to know which values are missing in this range.
For example, the table consists these values: 0, 1, 3, 4, 5, 7, 8, 9.
The query should return: 2, 6, 10.
[F5] solution (assuming sql server):
-- table with id=0..10
drop table #temp
GO
create table #temp (
id int not null identity(0,1),
x int
)
GO
insert into #temp (x) values(0)
GO 11
-- your number:
drop table #numbers
GO
select
*
into #numbers
from (
select 0 as n union all select 1 union all select 3 union all select 4 union all select 5 union all select 7 union all select 8 union all select 9
) x
GO
-- result:
select
*
from #temp t
left join #numbers n
on t.id=n.n
where 1=1
and n.n is null
This solution uses SQL-Server-Syntax (but AFAIK only GO is specific to the SQL Server Management Studio)
I would join against a table valued function that gets you all numbers in a certain range (example fiddle):
CREATE FUNCTION dbo.GetNumbersInRange(#Min INT, #Max INT)
RETURNS #trackingItems TABLE (Number INT)
AS BEGIN
DECLARE #counter INT = #Min
WHILE (#counter <= #Max)
BEGIN
INSERT INTO #trackingItems (Number) SELECT #counter
SELECT #counter = #counter + 1
END
RETURN
END
GO
As an example I have set up a table that contains some numbers (with gaps)
CREATE TABLE MyNumbers (Number INT)
INSERT INTO MyNumbers (Number)
SELECT 1
UNION
SELECT 2
UNION
SELECT 4
UNION
SELECT 5
UNION
SELECT 7
UNION
SELECT 8
To find the missing numbers you can use a LEFT JOIN like this
SELECT
AllNumbers.Number
FROM GetNumbersInRange(1, 10) AS AllNumbers
LEFT JOIN MyNumbers ON AllNumbers.Number = MyNumbers.Number
WHERE MyNumbers.Number IS NULL

SQL: how to get random number of rows from one table for each row in another

I have two tables where the data is not related
For each row in table A i want e.g. 3 random rows in table B
This is fairly easy using a cursor, but it is awfully slow
So how can i express this in single statement to avoid RBAR ?
To get a random number between 0 and (N-1), you can use.
abs(checksum(newid())) % N
Which means to get positive values 1-N, you use
1 + abs(checksum(newid())) % N
Note: RAND() doesn't work - it is evaluated once per query batch and you get stuck with the same value for all rows of tableA.
The query:
SELECT *
FROM tableA A
JOIN (select *, rn=row_number() over (order by newid())
from tableB) B ON B.rn <= 1 + abs(checksum(newid())) % 9
(assuming you wanted up to 9 random rows of B per A)
assuming tableB has integer surrogate key, try
Declare #maxRecs integer = 11 -- Maximum number of b records per a record
Select a.*, b.*
From tableA a Join tableB b
On b.PKColumn % (floor(Rand() * #maxRecs)) = 0
If you have a fixed number that you know in advance (such as 3), then:
select a.*, b.*
from a cross join
(select top 3 * from b) b
If you want a random number of rows from "b" for each row in "a", the problem is a bit harder in SQL Server.
Heres an example of how this could be done, code is self contained, copy and press F5 ;)
-- create two tables we can join
DECLARE #datatable TABLE(ID INT)
DECLARE #randomtable TABLE(ID INT)
-- add some dummy data
DECLARE #i INT = 1
WHILE(#i < 3) BEGIN
INSERT INTO #datatable (ID) VALUES (#i)
SET #i = #i + 1
END
SET #i = 1
WHILE(#i < 100) BEGIN
INSERT INTO #randomtable (ID) VALUES (#i)
SET #i = #i + 1
END
--The key here being the ORDER BY newid() which makes sure that
--the TOP 3 is different every time
SELECT
d.ID AS DataID
,rtable.ID RandomRow
FROM #datatable d
LEFT JOIN (SELECT TOP 3 * FROM #randomtable ORDER BY newid()) as rtable ON 1 = 1
Heres an example of the output

How can I extend this SQL query to find the k nearest neighbors?

I have a database full of two-dimensional data - points on a map. Each record has a field of the geometry type. What I need to be able to do is pass a point to a stored procedure which returns the k nearest points (k would also be passed to the sproc, but that's easy). I've found a query at http://blogs.msdn.com/isaac/archive/2008/10/23/nearest-neighbors.aspx which gets the single nearest neighbour, but I can't figure how to extend it to find the k nearest neighbours.
This is the current query - T is the table, g is the geometry field, #x is the point to search around, Numbers is a table with integers 1 to n:
DECLARE #start FLOAT = 1000;
WITH NearestPoints AS
(
SELECT TOP(1) WITH TIES *, T.g.STDistance(#x) AS dist
FROM Numbers JOIN T WITH(INDEX(spatial_index))
ON T.g.STDistance(#x) < #start*POWER(2,Numbers.n)
ORDER BY n
)
SELECT TOP(1) * FROM NearestPoints
ORDER BY n, dist
The inner query selects the nearest non-empty region and the outer query then selects the top result from that region; the outer query can easily be changed to (e.g.) SELECT TOP(20), but if the nearest region only contains one result, you're stuck with that.
I figure I probably need to recursively search for the first region containing k records, but without using a table variable (which would cause maintenance problems as you have to create the table structure and it's liable to change - there're lots of fields), I can't see how.
What happens if you remove TOP (1) WITH TIES from the inner query, and set the outer query to return the top k rows?
I'd also be interested to know whether this amendment helps at all. It ought to be more efficient than using TOP:
DECLARE #start FLOAT = 1000
,#k INT = 20
,#p FLOAT = 2;
WITH NearestPoints AS
(
SELECT *
,T.g.STDistance(#x) AS dist
,ROW_NUMBER() OVER (ORDER BY T.g.STDistance(#x)) AS rn
FROM Numbers
JOIN T WITH(INDEX(spatial_index))
ON T.g.STDistance(#x) < #start*POWER(#p,Numbers.n)
AND (Numbers.n - 1 = 0
OR T.g.STDistance(#x) >= #start*POWER(#p,Numbers.n - 1)
)
)
SELECT *
FROM NearestPoints
WHERE rn <= #k;
NB - untested - I don't have access to SQL 2008 here.
Quoted from Inside Microsoft® SQL Server® 2008: T-SQL Programming. Section 14.8.4.
The following query will return the 10
points of interest nearest to #input:
DECLARE #input GEOGRAPHY = 'POINT (-147 61)';
DECLARE #start FLOAT = 1000;
WITH NearestNeighbor AS(
SELECT TOP 10 WITH TIES
*, b.GEOG.STDistance(#input) AS dist
FROM Nums n JOIN GeoNames b WITH(INDEX(geog_hhhh_16_sidx)) -- index hint
ON b.GEOG.STDistance(#input) < #start*POWER(CAST(2 AS FLOAT),n.n)
AND b.GEOG.STDistance(#input) >=
CASE WHEN n = 1 THEN 0 ELSE #start*POWER(CAST(2 AS FLOAT),n.n-1) END
WHERE n <= 20
ORDER BY n
)
SELECT TOP 10 geonameid, name, feature_code, admin1_code, dist
FROM NearestNeighbor
ORDER BY n, dist;
Note: Only part of this query’s WHERE
clause is supported by the spatial
index. However, the query optimizer
correctly evaluates the supported part
(the "<" comparison) using the index.
This restricts the number of rows for
which the ">=" part must be tested,
and the query performs well. Changing
the value of #start can sometimes
speed up the query if it is slower
than desired.
Listing 2-1. Creating and Populating Auxiliary Table of Numbers
SET NOCOUNT ON;
USE InsideTSQL2008;
IF OBJECT_ID('dbo.Nums', 'U') IS NOT NULL DROP TABLE dbo.Nums;
CREATE TABLE dbo.Nums(n INT NOT NULL PRIMARY KEY);
DECLARE #max AS INT, #rc AS INT;
SET #max = 1000000;
SET #rc = 1;
INSERT INTO Nums VALUES(1);
WHILE #rc * 2 <= #max
BEGIN
INSERT INTO dbo.Nums SELECT n + #rc FROM dbo.Nums;
SET #rc = #rc * 2;
END
INSERT INTO dbo.Nums
SELECT n + #rc FROM dbo.Nums WHERE n + #rc <= #max;