Divide two numbers to a range without using parts - sql

I have to divide two numbers based on the increment provided by the users, I found an answer which is based on the parts.
I can not use parts as it effects numbers of rows in result, rather I have to pass increment.
Here is my query using parts.
declare #min numeric(18,0)
declare #max numeric(18,0)
declare #parts numeric(18,0)
select #min = 100 ,
#max = 204,
#parts = 10
declare #increment int = (#max - #min) / #parts
while #max >= #min
begin
declare #newMin numeric(18,0) = #min + #increment
print convert(varchar, #min) + ' - ' + convert(varchar, #newMin) select #min = #newMin + 1
end
Expected output, as you can see in the query my input is min and max with parts based on which the increments are calculating but I have to fix increment like 10 or 100.
From To
--------
100 110
111 121
122 132
133 143
144 154
155 165
166 176
177 187
188 198
199 204

There are 2 answers here, 1 based on the original version of the question (where To could be larger than #max) and that your goal is that #parts is the value of numbers in you want in the bucket (+1). The latter is that you want to split the range into that many buckets, and the last bucket is shrunk if the upper value is larger than the #max value. Both use a Tally function here, which I include the definition of:
CREATE FUNCTION [fn].[Tally] (#End bigint, #StartAtOne bit)
RETURNS table
AS RETURN
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT 0 AS I
WHERE #StartAtOne = 0
UNION ALL
SELECT TOP (#End)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3, N N4, N N5, N N6, N N7, N N8)
SELECT I
FROM Tally;
GO
DECLARE #min numeric(18,0) = 100,
#max numeric(18,0) = 304,
#parts numeric(18,0) = 10;
SELECT #min + (T.I * (#parts+1)),
#min + ((T.I+1) * (#parts+1))-1
FROM fn.Tally((#max - #min)/(#parts+1),0) T;
SELECT #min + (T.I * CEILING(((#max - #min)/#parts))),
CASE WHEN #min + ((T.I+1)* CEILING(((#max - #min)/#parts)))-1 > #max THEN #max ELSE #min + ((T.I+1)* CEILING(((#max - #min)/#parts)))-1 END
FROM fn.Tally(#parts-1,0) T
db<>fiddle

The question is unclear and doesn't describe the actual problem that needs solving. The code shows a way to partition a continuous range of numbers 100-204 into N partitions, in this case specified by #parts. It's not very efficient but it works.
Partitioning a range into parts is a popular SQL puzzle so there are a lot of articles over the past 30-40 years that show how to do it for different databases, using different features and trying to get the best performance. In the simple form there are two ways to partition :
By part count, what you have
By part size
If you don't want by part count, you probably want by part size. Doing this isn't complicated either and doesn't require slow loops.
Assuming we have a Tally table named Numbers, with all numbers up to eg 1M, the query to partition a range would be :
declare #start int=100
declare #end int=204
declare #size int=25
;with parts as (
select Number,(Number-#start)/#size as part_id
from numbers where number between #start and #end
)
select part_id,min(number) as [Start],max(number) as [End]
from parts
group by part_id
----------------------
part_id Start End
0 100 124
1 125 149
2 150 174
3 175 199
4 200 204
First, the Number is divided by the size we want using integer division to determine the part it belongs to. The results are then grouped by the Part ID and the range limits are the minimum and maximum Number in that group.
Creating a Numbers table is cheap. I used this script to generate a table with 1M numbers which takes about 11MB only:
DECLARE #UpperBound INT = 1000000;
;WITH cteN(Number) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id]) - 1
FROM sys.all_columns AS s1
CROSS JOIN sys.all_columns AS s2
)
SELECT [Number] INTO dbo.Numbers
FROM cteN WHERE [Number] <= #UpperBound;
CREATE UNIQUE CLUSTERED INDEX CIX_Number ON dbo.Numbers([Number])
WITH
(
FILLFACTOR = 100,
DATA_COMPRESSION = ROW
);
Data compression is available in all SQL Server versions and editions since SQL Server 2016 SP1.
The same technique can be used to partition a range into N parts, using the NTILE function this time :
declare #parts int=10
;with parts as (
select number, NTILE(#parts) over(order by number) as part_id
from numbers where number between #start and #end
)
select part_id,min(number) as [Start],max(number) as [End]
from parts
group by part_id
In real business cases NTILE is used to partition results into "buckets"

Related

TSQL - Insert rows based off range of integers

I am creating a SPROC that will take a number as a parameter. I need to take this number and get 2 numbers before it and 2 numbers after it and then insert those into a temp table. For example - The int is 355. I need to write an insert statemenet that inserts 353, 354, 355, 356 and 357. (-2 & +2). I am not entirely sure how to do this. I was thinking maybe a cursor? In C# I would do a for loop but I am not sure the right way to approach in a set language. Thanks!
Here is what I have so far:
CREATE PROCEDURE [dbo].[GetLanePrediction]
#startzip int
AS
BEGIN
--SET NOCOUNT ON;
DECLARE #posnegval int = 2
DECLARE #TempZips TABLE (ID INT IDENTITY(1,1), Zip INT)
--INSERT INTO #TempZips (Zip)
--Some kind of for loop or cursor here?
END
The output would create a temp table (#TempZips) with 5 rows. Like so:
ID Zip
1 353
2 354
3 355
4 356
5 357
Here is one simple method based on the question:
CREATE PROCEDURE [dbo].[GetLanePrediction] (
#onenumber int
) AS
BEGIN
DECLARE #TempZips TABLE (ID INT IDENTITY(1,1), Zip INT)
INSERT INTO #TempZips (Zip)
SELECT one_number + v.n
FROM (VALUES (-2), (-1), (0), (1), (2)) v(n);
END;
EDIT:
You can also use a recursive CTE:
;WITH n as (
SELECT -#posnegval as n
UNION ALL
SELECT n + 1
FROM n
WHERE n < #posnegval
)
INSERT INTO #TempZips (Zip)
SELECT #onenumber + n.n
FROM n
-- WITH OPTION (maxrecursion 0); -- only needed if you'll ever have more than 100 numbers
Just another option using an ad-hoc tally table
Declare #I int =355
Declare #R int =2
Select ID = N
,Zip= -1+#I-#R+N
From ( Select Top ((#R*2)+1) N=Row_Number() Over (Order By (Select NULL)) From master..spt_values n1 ) A
Returns
ID Zip
1 353
2 354
3 355
4 356
5 357

Uniformly distributed random

I wonder if someone knows how can I generate in Sql Server random values within a range uniformly distributed. This is what I did:
SELECT ID, AlgorithmType, AlgorithmID
FROM TEvaluateAlgorithm
I want AlgorithmID takes values from 0 to 15, has to be uniformly distributed
UPDATE TEA SET TEA.AlgorithmID = FLOOR(RAND(CONVERT(VARBINARY, NEWID()))*(16))
-- FROM TEvaluateAlgorithm TEA
I do not know what happen with the random, but is not distributing uniform random values between 0 and 15, not with the same amount.
For example from 0 to 9 is greater than from 10 to 15.
Thanks in advance!
EDITED:
Here is my data you can see the difference...
AlgorithmID COUNT(*)
0 22254
1 22651
2 22806
3 22736
4 22670
5 22368
6 22690
7 22736
8 22646
9 22536
10 14479
11 14787
12 14553
13 14546
14 14574
15 14722
rand() doesn't do a good job with this. Because you want integers, I would suggest the following:
select abs(checksum(newid()) % 16
I just checked this using:
select val, count(*)
from (select abs(checksum(newid()) % 16
from master..spt_values
) t
group by val
order by val;
and the distribution looks reasonable.
Here's a quick proof of concept.
Set #Loops to something big enough to make the statistics meaningful. 50k seems like a decent starting point.
Set #MinValue to the lowest integer in your set and set #TotalValues to how many integers you want in your set. 0 and 16 get you the 16 values [0-15], as noted in the question.
We're going to use a random function to cram 50k outputs into a temp table, then run some stats on it...
DECLARE #MinValue int
DECLARE #TotalValues int
SET #MinValue = 0
SET #TotalValues = 16
DECLARE #LoopCounter bigint
SET #LoopCounter = 0
DECLARE #Loops bigint
SET #Loops = 50000
CREATE TABLE #RandomValues
(
RandValue int
)
WHILE #LoopCounter < #Loops
BEGIN
INSERT INTO #RandomValues (RandValue) VALUES (FLOOR(RAND()*(#TotalValues-#MinValue)+#MinValue))
--you can plug into the right side of the above equation any other randomize formula you want to test
SET #LoopCounter = #LoopCounter + 1
END
--raw data query
SELECT
RandValue AS [Value],
COUNT(RandValue) AS [Occurrences],
((CONVERT(real, COUNT(RandValue))) / CONVERT(real, #Loops)) * 100.0 AS [Percentage]
FROM
#RandomValues
GROUP BY
RandValue
ORDER BY
RandValue ASC
--stats on your random query
SELECT
MIN([Percentage]) AS [Min %],
MAX([Percentage]) AS [Max %],
STDEV([Percentage]) AS [Standard Deviation]
FROM
(
SELECT
RandValue AS [Value],
COUNT(RandValue) AS [Occurrences],
((CONVERT(real, COUNT(RandValue))) / CONVERT(real, #Loops)) * 100.0 AS [Percentage]
FROM
#RandomValues
GROUP BY
RandValue
--ORDER BY
-- RandValue ASC
) DerivedRawData
DROP TABLE #RandomValues
Note that you can plug in any other randomizing formula into the right side of the INSERT statement within the WHILE loop then re-run to see if you like the results better. "Evenly distributed" is kinda subjective, but the standard deviation result is quantifiable and you can determine if it is acceptable or not.

Split string in SQL Server to a maximum length, returning each as a row

Is there a way to split a string (from a specific column) to n-number chars without breaking words, with each result in its own row?
Example:
2012-04-24 Change request #3 for the contract per terms and conditions and per John Smith in the PSO department Customer states terms should be Net 60 not Net 30. Please review signed contract for this information.
Results:
2012-04-24 Change request #3 for the contract per terms and conditions and per John Smith in the
PSO department Customer states terms should be Net 60 not Net 30.
Please review signed contract for this information.
I know I can use charindex to find the last space, but im not sure how i can get the remaining ones and return them as rows.
Try something like this. May be your can create a SQL function of following implementation.
DECLARE #Str VARCHAR(1000)
SET #Str = '2012-04-24 Change request #3 for the contract per terms and conditions and per John Smith in the PSO department Customer states terms should be Net 60 not Net 30. Please review signed contract for this information.'
DECLARE #End INT
DECLARE #Split INT
SET #Split = 100
declare #SomeTable table
(
Content varchar(3000)
)
WHILE (LEN(#Str) > 0)
BEGIN
IF (LEN(#Str) > #Split)
BEGIN
SET #End = LEN(LEFT(#Str, #Split)) - CHARINDEX(' ', REVERSE(LEFT(#Str, #Split)))
INSERT INTO #SomeTable VALUES (RTRIM(LTRIM(LEFT(LEFT(#Str, #Split), #End))))
SET #Str = SUBSTRING(#Str, #End + 1, LEN(#Str))
END
ELSE
BEGIN
INSERT INTO #SomeTable VALUES (RTRIM(LTRIM(#Str)))
SET #Str = ''
END
END
SELECT *
FROM #SomeTable
Output will be like this:
2012-04-24 Change request #3 for the contract per terms and conditions and per John Smith in the
PSO department Customer states terms should be Net 60 not Net 30. Please review signed contract
for this information.
I read some articles and each of them has error or bad performance or not working in small or big length of chunk we want. You can read my comments even in this article below of any answer. Finally i found a good answer and decided to share it in this question. I didn't check performance in various scenarios but i think is acceptable and working fine for small and big chunk length.
This is the code:
CREATE function SplitString
(
#str varchar(max),
#length int
)
RETURNS #Results TABLE( Result varchar(50),Sequence INT )
AS
BEGIN
DECLARE #Sequence INT
SET #Sequence = 1
DECLARE #s varchar(50)
WHILE len(#str) > 0
BEGIN
SET #s = left(#str, #length)
INSERT #Results VALUES (#s,#Sequence)
IF(len(#str)<#length)
BREAK
SET #str = right(#str, len(#str) - #length)
SET #Sequence = #Sequence + 1
END
RETURN
END
and source is #Rhyno answer on this question: TSQL UDF To Split String Every 8 Characters
Hope this help.
Just to see if it could be done, I came up with a solution that doesn't loop. It's based on somebody else's function to split a string based on a delimiter.
Note:
This requires that you know the maximum token length ahead of time. The function will stop returning lines upon encountering a token longer than the specified line length. There are probably other bugs lurking as well, so use this code at your own caution.
CREATE FUNCTION SplitLines
(
#pString VARCHAR(7999),
#pLineLen INT,
#pDelim CHAR(1)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
WITH
E1(N) AS ( --=== Create Ten 1's
SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 --10
),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --100
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10,000
cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT N)) FROM E4),
lines AS (
SELECT TOP 1
1 as LineNumber,
ltrim(rtrim(SUBSTRING(#pString, 1, N))) as Line,
N + 1 as start
FROM cteTally
WHERE N <= DATALENGTH(#pString) + 1
AND N <= #pLineLen + 1
AND SUBSTRING(#pString + #pDelim, N, 1) = #pDelim
ORDER BY N DESC
UNION ALL
SELECT LineNumber, Line, start
FROM (
SELECT LineNumber + 1 as LineNumber,
ltrim(rtrim(SUBSTRING(#pString, start, N))) as Line,
start + N + 1 as start,
ROW_NUMBER() OVER (ORDER BY N DESC) as r
FROM cteTally, lines
WHERE N <= DATALENGTH(#pString) + 1 - start
AND N <= #pLineLen
AND SUBSTRING(#pString + #pDelim, start + N, 1) = #pDelim
) A
WHERE r = 1
)
SELECT LineNumber, Line
FROM lines
It's actually quite fast and you can do cool things like join on it. Here's a simple example that gets the first 'line' from every row in a table:
declare #table table (
id int,
paragraph varchar(7999)
)
insert into #table values (1, '2012-04-24 Change request #3 for the contract per terms and conditions and per John Smith in the PSO department Customer states terms should be Net 60 not Net 30. Please review signed contract for this information.')
insert into #table values (2, 'Is there a way to split a string (from a specific column) to n-number chars without breaking words, with each result in its own row?')
select t.id, l.LineNumber, l.Line, len(Line)
from #table t
cross apply SplitLines(t.paragraph, 42, ' ') l
where l.LineNumber = 1
I know this is a bit late but a recursive cte would allow to achieve this.
Also you could make use of a seed table containing a sequence of numbers to feed into the substring as a multiplier for the start index.

Generate random int value from 3 to 6

Is it possible in Microsoft SQL Server generate random int value from Min to Max (3-9 example, 15-99 e.t.c)
I know, I can generate from 0 to Max, but how to increase Min border?
This query generate random value from 1 to 6. Need to change it from 3 to 6.
SELECT table_name, 1.0 + floor(6 * RAND(convert(varbinary, newid()))) magic_number
FROM information_schema.tables
Added 5 sec later:
SELECT table_name, 3.0 + floor(4 * RAND(convert(varbinary, newid()))) magic_number
FROM information_schema.tables
A helpful editor added the 'Select' before each statement but the point of this item is that it can generate unique keys for each row in a return, not just one item (For that I would us the Rand() function).
For example:
Select top 100 Rand(),* from tblExample
Would return the same random value for all 100 rows.
While:
Select top 100 ABS(CHECKSUM(NEWID()) % 10),* from tblexample
Would return a different random value between 0 and 9 on each row in the return.
So while the select makes it easier to copy and paste, you can copy the logic into a select statement if that is what is required.
This generates a random number between 0-9
SELECT ABS(CHECKSUM(NEWID()) % 10)
1 through 6
SELECT ABS(CHECKSUM(NEWID()) % 6) + 1
3 through 6
SELECT ABS(CHECKSUM(NEWID()) % 4) + 3
Dynamic (Based on Eilert Hjelmeseths Comment - thanks to jiraiya for providing the visual presentation)
SELECT ABS(CHECKSUM(NEWID()) % (#max - #min + 1)) + #min
Updated based on comments:
NEWID generates random string (for each row in return)
CHECKSUM takes value of string and creates number
modulus (%) divides by that number and returns the remainder (meaning max value is one less than the number you use)
ABS changes negative results to positive
then add one to the result to eliminate 0 results (to simulate a dice roll)
I see you have added an answer to your question in SQL Server 2008 you can also do
SELECT 3 + CRYPT_GEN_RANDOM(1) % 4 /*Random number between 3 and 6*/
FROM ...
A couple of disadvantages of this method are
This is slower than the NEWID() method
Even though it is evaluated once per row the query optimiser does not realise this which can lead to odd results.
but just thought I'd add it as another option.
You can do this:
DECLARE #maxval TINYINT, #minval TINYINT
select #maxval=24,#minval=5
SELECT CAST(((#maxval + 1) - #minval) *
RAND(CHECKSUM(NEWID())) + #minval AS TINYINT)
And that was taken directly from this link, I don't really know how to give proper credit for this answer.
Here is the simple and single line of code
For this use the SQL Inbuild RAND() function.
Here is the formula to generate random number between two number (RETURN INT Range)
Here a is your First Number (Min) and b is the Second Number (Max) in Range
SELECT FLOOR(RAND()*(b-a)+a)
Note: You can use CAST or CONVERT function as well to get INT range number.
( CAST(RAND()*(25-10)+10 AS INT) )
Example:
SELECT FLOOR(RAND()*(25-10)+10);
Here is the formula to generate random number between two number (RETURN DECIMAL Range)
SELECT RAND()*(b-a)+a;
Example:
SELECT RAND()*(25-10)+10;
More details check this: https://www.techonthenet.com/sql_server/functions/rand.php
Simply:
DECLARE #MIN INT=3; --We define minimum value, it can be generated.
DECLARE #MAX INT=6; --We define maximum value, it can be generated.
SELECT #MIN+FLOOR((#MAX-#MIN+1)*RAND(CONVERT(VARBINARY,NEWID()))); --And then this T-SQL snippet generates an integer between minimum and maximum integer values.
You can change and edit this code for your needs.
Nice and simple, from Pinal Dave's site:
http://blog.sqlauthority.com/2007/04/29/sql-server-random-number-generator-script-sql-query/
DECLARE #Random INT;
DECLARE #Upper INT;
DECLARE #Lower INT
SET #Lower = 3 ---- The lowest random number
SET #Upper = 7 ---- One more than the highest random number
SELECT #Random = ROUND(((#Upper - #Lower -1) * RAND() + #Lower), 0)
SELECT #Random
(I did make a slight change to the #Upper- to include the upper number, added 1.)
In general:
select rand()*(#upper-#lower)+#lower;
For your question:
select rand()*(6-3)+3;
<=>
select rand()*3+3;
SELECT ROUND((6 - 3 * RAND()), 0)
Lamak's answer as a function:
-- Create RANDBETWEEN function
-- Usage: SELECT dbo.RANDBETWEEN(0,9,RAND(CHECKSUM(NEWID())))
CREATE FUNCTION dbo.RANDBETWEEN(#minval TINYINT, #maxval TINYINT, #random NUMERIC(18,10))
RETURNS TINYINT
AS
BEGIN
RETURN (SELECT CAST(((#maxval + 1) - #minval) * #random + #minval AS TINYINT))
END
GO
DECLARE #min INT = 3;
DECLARE #max INT = 6;
SELECT #min + ROUND(RAND() * (#max - #min), 0);
Step by step
DECLARE #min INT = 3;
DECLARE #max INT = 6;
DECLARE #rand DECIMAL(19,4) = RAND();
DECLARE #difference INT = #max - #min;
DECLARE #chunk INT = ROUND(#rand * #difference, 0);
DECLARE #result INT = #min + #chunk;
SELECT #result;
Note that a user-defined function thus not allow the use of RAND(). A workaround for this (source: http://blog.sqlauthority.com/2012/11/20/sql-server-using-rand-in-user-defined-functions-udf/) is to create a view first.
CREATE VIEW [dbo].[vw_RandomSeed]
AS
SELECT RAND() AS seed
and then create the random function
CREATE FUNCTION udf_RandomNumberBetween
(
#min INT,
#max INT
)
RETURNS INT
AS
BEGIN
RETURN #min + ROUND((SELECT TOP 1 seed FROM vw_RandomSeed) * (#max - #min), 0);
END

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;