У меня есть два числа в качестве ввода от пользователя, например, 1000
и 1050
.
Как мне сгенерировать числа между этими двумя числами, используя sql-запрос, в отдельных строках? Я хочу это:
1000
1001
1002
1003
.
.
1050
У меня есть два числа в качестве ввода от пользователя, например, 1000
и 1050
.
Как мне сгенерировать числа между этими двумя числами, используя sql-запрос, в отдельных строках? Я хочу это:
1000
1001
1002
1003
.
.
1050
Выберите непостоянные значения с помощью ключевого слова VALUES
. Затем используйте JOIN
для генерации множества комбинаций (можно расширить для создания сотен тысяч строк и более).
SELECT ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
WHERE ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n BETWEEN @userinput1 AND @userinput2
ORDER BY 1
Более короткая альтернатива, которую не так легко понять:
WITH x AS (SELECT n FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) v(n))
SELECT ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n
FROM x ones, x tens, x hundreds, x thousands
ORDER BY 1
альтернативным решением является рекурсивный CTE:
DECLARE @startnum INT=1000
DECLARE @endnum INT=1050
;
WITH gen AS (
SELECT @startnum AS num
UNION ALL
SELECT num+1 FROM gen WHERE num+1<[email protected]
)
SELECT * FROM gen
option (maxrecursion 10000)
SELECT DISTINCT n = number
FROM master..[spt_values]
WHERE number BETWEEN @start AND @end
Обратите внимание, что эта таблица имеет максимум 2048, потому что тогда числа имеют пробелы.
Здесь немного лучше использовать системный вид (начиная с SQL-Server 2005):
;WITH Nums AS
(
SELECT n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects
)
SELECT n FROM Nums
WHERE n BETWEEN @start AND @end
ORDER BY n;
или используйте пользовательскую таблицу номеров. Кредиты Аарону Бертрану, я предлагаю прочитать всю статью: Создать набор или последовательность без циклов
Недавно я написал эту встроенную функцию оценки таблицы, чтобы решить эту проблему. Он не ограничен в других областях, кроме памяти и хранилища. Он не обращается к таблицам, поэтому нет необходимости читать или записывать диски вообще. Он добавляет значения объединений экспоненциально на каждой итерации, поэтому он очень быстр даже для очень больших диапазонов. Он создает десять миллионов записей за пять секунд на моем сервере. Он также работает с отрицательными значениями.
CREATE FUNCTION [dbo].[fn_ConsecutiveNumbers]
(
@start int,
@end int
) RETURNS TABLE
RETURN
select
x268435456.X
| x16777216.X
| x1048576.X
| x65536.X
| x4096.X
| x256.X
| x16.X
| x1.X
+ @start
X
from
(VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15)) as x1(X)
join
(VALUES (0),(16),(32),(48),(64),(80),(96),(112),(128),(144),(160),(176),(192),(208),(224),(240)) as x16(X)
on x1.X <= @[email protected] and x16.X <= @[email protected]
join
(VALUES (0),(256),(512),(768),(1024),(1280),(1536),(1792),(2048),(2304),(2560),(2816),(3072),(3328),(3584),(3840)) as x256(X)
on x256.X <= @[email protected]
join
(VALUES (0),(4096),(8192),(12288),(16384),(20480),(24576),(28672),(32768),(36864),(40960),(45056),(49152),(53248),(57344),(61440)) as x4096(X)
on x4096.X <= @[email protected]
join
(VALUES (0),(65536),(131072),(196608),(262144),(327680),(393216),(458752),(524288),(589824),(655360),(720896),(786432),(851968),(917504),(983040)) as x65536(X)
on x65536.X <= @[email protected]
join
(VALUES (0),(1048576),(2097152),(3145728),(4194304),(5242880),(6291456),(7340032),(8388608),(9437184),(10485760),(11534336),(12582912),(13631488),(14680064),(15728640)) as x1048576(X)
on x1048576.X <= @[email protected]
join
(VALUES (0),(16777216),(33554432),(50331648),(67108864),(83886080),(100663296),(117440512),(134217728),(150994944),(167772160),(184549376),(201326592),(218103808),(234881024),(251658240)) as x16777216(X)
on x16777216.X <= @[email protected]
join
(VALUES (0),(268435456),(536870912),(805306368),(1073741824),(1342177280),(1610612736),(1879048192)) as x268435456(X)
on x268435456.X <= @[email protected]
WHERE @end >=
x268435456.X
| isnull(x16777216.X, 0)
| isnull(x1048576.X, 0)
| isnull(x65536.X, 0)
| isnull(x4096.X, 0)
| isnull(x256.X, 0)
| isnull(x16.X, 0)
| isnull(x1.X, 0)
+ @start
GO
SELECT X FROM fn_ConsecutiveNumbers(5, 500);
Он также подходит для диапазонов даты и времени:
SELECT DATEADD(day,X, 0) DayX
FROM fn_ConsecutiveNumbers(datediff(day,0,'5/8/2015'), datediff(day,0,'5/31/2015'))
SELECT DATEADD(hour,X, 0) HourX
FROM fn_ConsecutiveNumbers(datediff(hour,0,'5/8/2015'), datediff(hour,0,'5/8/2015 12:00 PM'));
Вы можете использовать крест, чтобы присоединиться к нему, чтобы разделить записи на основе значений в таблице. Например, чтобы создать запись за каждую минуту в диапазоне времени в таблице, вы можете сделать что-то вроде:
select TimeRanges.StartTime,
TimeRanges.EndTime,
DATEADD(minute,X, 0) MinuteX
FROM TimeRanges
cross apply fn_ConsecutiveNumbers(datediff(hour,0,TimeRanges.StartTime),
datediff(hour,0,TimeRanges.EndTime)) ConsecutiveNumbers
Самый лучший вариант, который я использовал, выглядит следующим образом:
DECLARE @min bigint, @max bigint
SELECT @Min=919859000000 ,@Max=919859999999
SELECT TOP (@[email protected]+1) @Min-1+row_number() over(order by t1.number) as N
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
Я создал миллионы записей, используя это, и он отлично работает.
Это работает для меня!
select top 50 ROW_NUMBER() over(order by a.name) + 1000 as Rcount
from sys.all_objects a
Если у вас нет проблемы с установкой сборки CLR на вашем сервере, хорошим вариантом является запись функции табличной оценки в .NET. Таким образом, вы можете использовать простой синтаксис, упрощая объединение с другими запросами, а в качестве бонуса не будет тратить память, потому что результат передается.
Создайте проект, содержащий следующий класс:
using System;
using System.Collections;
using System.Data;
using System.Data.Sql;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
namespace YourNamespace
{
public sealed class SequenceGenerator
{
[SqlFunction(FillRowMethodName = "FillRow")]
public static IEnumerable Generate(SqlInt32 start, SqlInt32 end)
{
int _start = start.Value;
int _end = end.Value;
for (int i = _start; i <= _end; i++)
yield return i;
}
public static void FillRow(Object obj, out int i)
{
i = (int)obj;
}
private SequenceGenerator() { }
}
}
Поместите узел где-нибудь на сервер и запустите:
USE db;
CREATE ASSEMBLY SqlUtil FROM 'c:\path\to\assembly.dll'
WITH permission_set=Safe;
CREATE FUNCTION [Seq](@start int, @end int)
RETURNS TABLE(i int)
AS EXTERNAL NAME [SqlUtil].[YourNamespace.SequenceGenerator].[Generate];
Теперь вы можете запустить:
select * from dbo.seq(1, 1000000)
2 года спустя, но я обнаружил, что у меня такая же проблема. Вот как я это решил. (отредактированный для включения параметров)
DECLARE @Start INT, @End INT
SET @Start = 1000
SET @End = 1050
SELECT TOP (@End - @Start+1) ROW_NUMBER() OVER (ORDER BY S.[object_id])+(@Start - 1) [Numbers]
FROM sys.all_objects S WITH (NOLOCK)
Лучший способ - использовать рекурсивные ctes.
declare @initial as int = 1000;
declare @final as int =1050;
with cte_n as (
select @initial as contador
union all
select contador+1 from cte_n
where contador <@final
) select * from cte_n option (maxrecursion 0)
Saludos.
Ничего нового, но я переписал решение Брайана Пресслера, чтобы было проще для глаз, оно могло бы быть полезным для кого-то (даже если оно только меня будущее):
alter function [dbo].[fn_GenerateNumbers]
(
@start int,
@end int
) returns table
return
with
b0 as (select n from (values (0),(0x00000001),(0x00000002),(0x00000003),(0x00000004),(0x00000005),(0x00000006),(0x00000007),(0x00000008),(0x00000009),(0x0000000A),(0x0000000B),(0x0000000C),(0x0000000D),(0x0000000E),(0x0000000F)) as b0(n)),
b1 as (select n from (values (0),(0x00000010),(0x00000020),(0x00000030),(0x00000040),(0x00000050),(0x00000060),(0x00000070),(0x00000080),(0x00000090),(0x000000A0),(0x000000B0),(0x000000C0),(0x000000D0),(0x000000E0),(0x000000F0)) as b1(n)),
b2 as (select n from (values (0),(0x00000100),(0x00000200),(0x00000300),(0x00000400),(0x00000500),(0x00000600),(0x00000700),(0x00000800),(0x00000900),(0x00000A00),(0x00000B00),(0x00000C00),(0x00000D00),(0x00000E00),(0x00000F00)) as b2(n)),
b3 as (select n from (values (0),(0x00001000),(0x00002000),(0x00003000),(0x00004000),(0x00005000),(0x00006000),(0x00007000),(0x00008000),(0x00009000),(0x0000A000),(0x0000B000),(0x0000C000),(0x0000D000),(0x0000E000),(0x0000F000)) as b3(n)),
b4 as (select n from (values (0),(0x00010000),(0x00020000),(0x00030000),(0x00040000),(0x00050000),(0x00060000),(0x00070000),(0x00080000),(0x00090000),(0x000A0000),(0x000B0000),(0x000C0000),(0x000D0000),(0x000E0000),(0x000F0000)) as b4(n)),
b5 as (select n from (values (0),(0x00100000),(0x00200000),(0x00300000),(0x00400000),(0x00500000),(0x00600000),(0x00700000),(0x00800000),(0x00900000),(0x00A00000),(0x00B00000),(0x00C00000),(0x00D00000),(0x00E00000),(0x00F00000)) as b5(n)),
b6 as (select n from (values (0),(0x01000000),(0x02000000),(0x03000000),(0x04000000),(0x05000000),(0x06000000),(0x07000000),(0x08000000),(0x09000000),(0x0A000000),(0x0B000000),(0x0C000000),(0x0D000000),(0x0E000000),(0x0F000000)) as b6(n)),
b7 as (select n from (values (0),(0x10000000),(0x20000000),(0x30000000),(0x40000000),(0x50000000),(0x60000000),(0x70000000)) as b7(n))
select s.n
from (
select
b7.n
| b6.n
| b5.n
| b4.n
| b3.n
| b2.n
| b1.n
| b0.n
+ @start
n
from b0
join b1 on b0.n <= @[email protected] and b1.n <= @[email protected]
join b2 on b2.n <= @[email protected]
join b3 on b3.n <= @[email protected]
join b4 on b4.n <= @[email protected]
join b5 on b5.n <= @[email protected]
join b6 on b6.n <= @[email protected]
join b7 on b7.n <= @[email protected]
) s
where @end >= s.n
GO
Вот пара вполне оптимальных и совместимых решений:
USE master;
declare @min as int; set @min = 1000;
declare @max as int; set @max = 1050; --null returns all
-- Up to 256 - 2 048 rows depending on SQL Server version
select isnull(@min,0)+number.number as number
FROM dbo.spt_values AS number
WHERE number."type" = 'P' --integers
and ( @max is null --return all
or isnull(@min,0)+number.number <= @max --return up to max
)
order by number
;
-- Up to 65 536 - 4 194 303 rows depending on SQL Server version
select isnull(@min,0)+value1.number+(value2.number*numberCount.numbers) as number
FROM dbo.spt_values AS value1
cross join dbo.spt_values AS value2
cross join ( --get the number of numbers (depends on version)
select sum(1) as numbers
from dbo.spt_values
where spt_values."type" = 'P' --integers
) as numberCount
WHERE value1."type" = 'P' --integers
and value2."type" = 'P' --integers
and ( @max is null --return all
or isnull(@min,0)+value1.number+(value2.number*numberCount.numbers)
<= @max --return up to max
)
order by number
;
ответ slartidan можно улучшить, повысить производительность, исключив все ссылки на декартовой продукт и вместо этого используя ROW_NUMBER()
(план выполнения сравнивается):
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n FROM
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x1(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x2(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x3(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x4(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x5(x)
ORDER BY n
Обмотайте его внутри CTE и добавьте предложение where для выбора нужных номеров:
DECLARE @n1 AS INT = 100;
DECLARE @n2 AS INT = 40099;
WITH numbers AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n FROM
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x1(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x2(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x3(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x4(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x5(x)
)
SELECT numbers.n
FROM numbers
WHERE n BETWEEN @n1 and @n2
ORDER BY n
Я знаю, что мне уже 4 года, но я наткнулся на еще один альтернативный ответ на эту проблему. Проблема скорости - это не только предварительная фильтрация, но и предотвращение сортировки. Это позволяет принудительно выполнить порядок соединения таким образом, чтобы декартово произведение действительно подсчитывалось в результате объединения. Использование slartidan-ответа в качестве точки перехода:
WITH x AS (SELECT n FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) v(n))
SELECT ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n
FROM x ones, x tens, x hundreds, x thousands
ORDER BY 1
Если мы знаем диапазон, который мы хотим, мы можем указать его через @Upper и @Lower. Объединив подсказку соединения REMOTE вместе с TOP, мы можем вычислить только подмножество значений, которые мы хотим, ничего не теряя.
WITH x AS (SELECT n FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) v(n))
SELECT TOP ([email protected]@Lower) @Lower + ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n
FROM x thousands
INNER REMOTE JOIN x hundreds on 1=1
INNER REMOTE JOIN x tens on 1=1
INNER REMOTE JOIN x ones on 1=1
Подсказка соединения REMOTE заставляет оптимизатор сначала сравнивать на правой стороне соединения. Указав каждое соединение как REMOTE от большинства до наименее значимого значения, само соединение будет правильно подсчитывать вверх одним. Не нужно фильтровать с помощью WHERE или сортировать с помощью ORDER BY.
Если вы хотите увеличить диапазон, вы можете продолжать добавлять дополнительные соединения с прогрессивно более высокими порядками, если они упорядочены от большинства до наименее значимых в предложении FROM.
Обратите внимание, что это запрос, специфичный для SQL Server 2008 или выше.
Это также сделает
DECLARE @startNum INT = 1000;
DECLARE @endNum INT = 1050;
INSERT INTO dbo.Numbers
( Num
)
SELECT CASE WHEN MAX(Num) IS NULL THEN @startNum
ELSE MAX(Num) + 1
END AS Num
FROM dbo.Numbers
GO 51
Лучшая скорость при выполнении запроса
DECLARE @num INT = 1000
WHILE(@num<1050)
begin
INSERT INTO [dbo].[Codes]
( Code
)
VALUES (@num)
SET @num = @num + 1
end
рекурсивный CTE в экспоненциальном размере (даже для 100 рекурсии по умолчанию это может создать до 2 ^ 100 номеров):
DECLARE @startnum INT=1000
DECLARE @endnum INT=1050
DECLARE @size [email protected]@startnum+1
;
WITH numrange (num) AS (
SELECT 1 AS num
UNION ALL
SELECT num*2 FROM numrange WHERE num*2<[email protected]
UNION ALL
SELECT num*2+1 FROM numrange WHERE num*2+1<[email protected]
)
SELECT [email protected] FROM numrange order by num
Мне пришлось вставить путь к файлу изображения в базу данных с помощью аналогичного метода. Следующий запрос работал нормально:
DECLARE @num INT = 8270058
WHILE(@num<8270284)
begin
INSERT INTO [dbo].[Galleries]
(ImagePath)
VALUES
('~/Content/Galeria/P'+CONVERT(varchar(10), @num)+'.JPG')
SET @num = @num + 1
end
Код для вас:
DECLARE @num INT = 1000
WHILE(@num<1051)
begin
SELECT @num
SET @num = @num + 1
end
-- Generate Numeric Range
-- Source: http://www.sqlservercentral.com/scripts/Miscellaneous/30397/
CREATE TABLE #NumRange(
n int
)
DECLARE @MinNum int
DECLARE @MaxNum int
DECLARE @I int
SET NOCOUNT ON
SET @I = 0
WHILE @I <= 9 BEGIN
INSERT INTO #NumRange VALUES(@I)
SET @I = @I + 1
END
SET @MinNum = 1
SET @MaxNum = 1000000
SELECT num = a.n +
(b.n * 10) +
(c.n * 100) +
(d.n * 1000) +
(e.n * 10000)
FROM #NumRange a
CROSS JOIN #NumRange b
CROSS JOIN #NumRange c
CROSS JOIN #NumRange d
CROSS JOIN #NumRange e
WHERE a.n +
(b.n * 10) +
(c.n * 100) +
(d.n * 1000) +
(e.n * 10000) BETWEEN @MinNum AND @MaxNum
ORDER BY a.n +
(b.n * 10) +
(c.n * 100) +
(d.n * 1000) +
(e.n * 10000)
DROP TABLE #NumRange
Это работает только для последовательностей, если в какой-либо таблице приложений есть строки. Предположим, что я хочу последовательность из 1..100 и имею таблицу приложений dbo.foo со столбцом (числового или строкового типа) foo.bar:
select
top 100
row_number() over (order by dbo.foo.bar) as seq
from dbo.foo
Несмотря на свое присутствие в предложении order by, dbo.foo.bar не должен иметь отдельных или даже ненулевых значений.
Конечно, SQL Server 2012 имеет объекты последовательности, поэтому в этом продукте есть естественное решение.
Это завершено для меня за 36 секунд на нашем сервере DEV. Как и ответ Брайана, фокусировка на фильтрации в диапазоне важна из запроса; BETWEEN все еще пытается сгенерировать все начальные записи до нижней границы, даже если они им не нужны.
declare @s bigint = 10000000
, @e bigint = 20000000
;WITH
Z AS (SELECT 0 z FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15)) T(n)),
Y AS (SELECT 0 z FROM Z a, Z b, Z c, Z d, Z e, Z f, Z g, Z h, Z i, Z j, Z k, Z l, Z m, Z n, Z o, Z p),
N AS (SELECT ROW_NUMBER() OVER (PARTITION BY 0 ORDER BY z) n FROM Y)
SELECT TOP ([email protected]@s) @s + n - 1 FROM N
Обратите внимание, что ROW_NUMBER является bigint, поэтому мы не можем переходить на 2 ^^ 64 (== 16 ^^ 16) сгенерированные записи любым способом, который использует Это. Поэтому этот запрос учитывает тот же верхний предел для сгенерированных значений.
Вот что я придумал:
create or alter function dbo.fn_range(@start int, @end int) returns table
return
with u2(n) as (
select n
from (VALUES (0),(1),(2),(3)) v(n)
),
u8(n) as (
select
x0.n | x1.n * 4 | x2.n * 16 | x3.n * 64 as n
from u2 x0, u2 x1, u2 x2, u2 x3
)
select
@start + s.n as n
from (
select
x0.n | isnull(x1.n, 0) * 256 | isnull(x2.n, 0) * 65536 as n
from u8 x0
left join u8 x1 on @[email protected] > 256
left join u8 x2 on @[email protected] > 65536
) s
where s.n < @end - @start
Генерирует до 2 ^ 24 значений. Условия соединения сохраняют это быстро при небольших значениях.
В этом случае используется процедурный код и табличная функция. Медленно, но легко и предсказуемо.
CREATE FUNCTION [dbo].[Sequence] (@start int, @end int)
RETURNS
@Result TABLE(ID int)
AS
begin
declare @i int;
set @i = @start;
while @i <= @end
begin
insert into @result values (@i);
set @i = @i+1;
end
return;
end
Использование:
SELECT * FROM dbo.Sequence (3,7);
ID
3
4
5
6
7
Это таблица, поэтому вы можете использовать ее в соединениях с другими данными. Я чаще всего использую эту функцию как левую сторону соединения против GROUP BY hour, day и т.д., Чтобы обеспечить непрерывную последовательность значений времени.
SELECT DateAdd(hh,ID,'2018-06-20 00:00:00') as HoursInTheDay FROM dbo.Sequence (0,23) ;
HoursInTheDay
2018-06-20 00:00:00.000
2018-06-20 01:00:00.000
2018-06-20 02:00:00.000
2018-06-20 03:00:00.000
2018-06-20 04:00:00.000
(...)
Производительность невыносима (16 секунд для миллиона строк), но достаточно для многих целей.
SELECT count(1) FROM [dbo].[Sequence] (
1000001
,2000000)
GO
Oracle 12c; Быстрый, но ограниченный:
select rownum+1000 from all_objects fetch first 50 rows only;
Примечание: ограничено количеством строк в представлении all_objects;
declare @start int = 1000 declare @end int = 1050
с номером
КАК
(
SELECT @start [SEQUENCE]
UNION все
SELECT [SEQUENCE] + 1 FROM numcte WHERE [SEQUENCE] <@end)
SELECT * FROM numcte
Это то, что я делаю, это довольно быстро и гибко, и не так много кода.
DECLARE @count int = 65536;
DECLARE @start int = 11;
DECLARE @xml xml = REPLICATE(CAST('<x/>' AS nvarchar(max)), @count);
; WITH GenerateNumbers(Num) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY @count) + @start - 1
FROM @xml.nodes('/x') X(T)
)
SELECT Num
FROM GenerateNumbers;
Обратите внимание, что (ORDER BY @count) - пустышка. Он ничего не делает, но ROW_NUMBER() требует ORDER BY.
Изменить: я понял, что первоначальный вопрос должен был получить диапазон от х до у. Мой скрипт может быть изменен так, чтобы получить диапазон:
DECLARE @start int = 5;
DECLARE @end int = 21;
DECLARE @xml xml = REPLICATE(CAST('<x/>' AS nvarchar(max)), @end - @start + 1);
; WITH GenerateNumbers(Num) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY @end) + @start - 1
FROM @xml.nodes('/x') X(T)
)
SELECT Num
FROM GenerateNumbers;