Хех ... извините, я так поздно отвечаю на старый пост. И да, я должен был ответить, потому что самый популярный ответ (в то время, ответ Recursive CTE со ссылкой на 14 различных методов) в этой теме, ммм ... производительность в лучшем случае ставится под сомнение.
Во-первых, статья с 14 различными решениями хороша для того, чтобы увидеть разные методы создания таблицы Numbers / Tally на лету, но, как указано в статье и в цитируемой теме, есть очень важная цитата ...
"предложения относительно эффективности и
производительность часто субъективна.
Независимо от того, как выполняется запрос
используется, физическая реализация
определяет эффективность запроса.
Поэтому вместо того, чтобы полагаться на
предвзятые руководящие принципы, это обязательно
что вы тестируете запрос и определяете
какой из них работает лучше. "
По иронии судьбы, сама статья содержит множество субъективных утверждений и «предвзятых указаний», таких как «рекурсивный CTE может генерировать список чисел довольно эффективно " и "Это эффективный метод использования цикла WHILE из публикации группы новостей Ицик Бен-Геном " (который, я уверен, он опубликовал только для сравнения). Да ладно, ребята ... Просто упоминание доброго имени Ицик может привести к тому, что какой-то плохой человек действительно использует этот ужасный метод. Автору следует практиковать то, что он проповедует, и проводить небольшое тестирование производительности, прежде чем делать такие смехотворно неверные заявления, особенно перед лицом любой масштабируемости.
С мыслью о том, чтобы действительно провести некоторое тестирование, прежде чем делать какие-либо субъективные заявления о том, что делает какой-либо код или что кому-то «нравится», вот код, с которым вы можете провести свое собственное тестирование. Настройте профилировщик для SPID, с которого вы запускаете тест, и проверьте его сами ... просто выполните "Search'n'Replace" с номером 1000000 для своего "любимого" номера и посмотрите ...
--===== Test for 1000000 rows ==================================
GO
--===== Traditional RECURSIVE CTE method
WITH Tally (N) AS
(
SELECT 1 UNION ALL
SELECT 1 + N FROM Tally WHERE N < 1000000
)
SELECT N
INTO #Tally1
FROM Tally
OPTION (MAXRECURSION 0);
GO
--===== Traditional WHILE LOOP method
CREATE TABLE #Tally2 (N INT);
SET NOCOUNT ON;
DECLARE @Index INT;
SET @Index = 1;
WHILE @Index <= 1000000
BEGIN
INSERT #Tally2 (N)
VALUES (@Index);
SET @Index = @Index + 1;
END;
GO
--===== Traditional CROSS JOIN table method
SELECT TOP (1000000)
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS N
INTO #Tally3
FROM Master.sys.All_Columns ac1
CROSS JOIN Master.sys.ALL_Columns ac2;
GO
--===== Itzik's CROSS JOINED CTE method
WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1),
E02(N) AS (SELECT 1 FROM E00 a, E00 b),
E04(N) AS (SELECT 1 FROM E02 a, E02 b),
E08(N) AS (SELECT 1 FROM E04 a, E04 b),
E16(N) AS (SELECT 1 FROM E08 a, E08 b),
E32(N) AS (SELECT 1 FROM E16 a, E16 b),
cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY N) FROM E32)
SELECT N
INTO #Tally4
FROM cteTally
WHERE N <= 1000000;
GO
--===== Housekeeping
DROP TABLE #Tally1, #Tally2, #Tally3, #Tally4;
GO
Пока мы здесь, вот числа, которые я получаю из SQL Profiler для значений 100, 1000, 10000, 100000 и 1000000 ...
SPID TextData Dur(ms) CPU Reads Writes
---- ---------------------------------------- ------- ----- ------- ------
51 --===== Test for 100 rows ============== 8 0 0 0
51 --===== Traditional RECURSIVE CTE method 16 0 868 0
51 --===== Traditional WHILE LOOP method CR 73 16 175 2
51 --===== Traditional CROSS JOIN table met 11 0 80 0
51 --===== Itzik's CROSS JOINED CTE method 6 0 63 0
51 --===== Housekeeping DROP TABLE #Tally 35 31 401 0
51 --===== Test for 1000 rows ============= 0 0 0 0
51 --===== Traditional RECURSIVE CTE method 47 47 8074 0
51 --===== Traditional WHILE LOOP method CR 80 78 1085 0
51 --===== Traditional CROSS JOIN table met 5 0 98 0
51 --===== Itzik's CROSS JOINED CTE method 2 0 83 0
51 --===== Housekeeping DROP TABLE #Tally 6 15 426 0
51 --===== Test for 10000 rows ============ 0 0 0 0
51 --===== Traditional RECURSIVE CTE method 434 344 80230 10
51 --===== Traditional WHILE LOOP method CR 671 563 10240 9
51 --===== Traditional CROSS JOIN table met 25 31 302 15
51 --===== Itzik's CROSS JOINED CTE method 24 0 192 15
51 --===== Housekeeping DROP TABLE #Tally 7 15 531 0
51 --===== Test for 100000 rows =========== 0 0 0 0
51 --===== Traditional RECURSIVE CTE method 4143 3813 800260 154
51 --===== Traditional WHILE LOOP method CR 5820 5547 101380 161
51 --===== Traditional CROSS JOIN table met 160 140 479 211
51 --===== Itzik's CROSS JOINED CTE method 153 141 276 204
51 --===== Housekeeping DROP TABLE #Tally 10 15 761 0
51 --===== Test for 1000000 rows ========== 0 0 0 0
51 --===== Traditional RECURSIVE CTE method 41349 37437 8001048 1601
51 --===== Traditional WHILE LOOP method CR 59138 56141 1012785 1682
51 --===== Traditional CROSS JOIN table met 1224 1219 2429 2101
51 --===== Itzik's CROSS JOINED CTE method 1448 1328 1217 2095
51 --===== Housekeeping DROP TABLE #Tally 8 0 415 0
Как видите, метод рекурсивного CTE является вторым худшим после цикла Loop для длительности и ЦП и в 8 раз превышает нагрузку на память в виде логических операций чтения, чем цикл Loop . Это RBAR на стероидах, и его следует избегать любой ценой для любых вычислений в одной строке, так же как следует избегать циклического цикла. Есть места, где рекурсия весьма ценна, но это НЕ одно из них .
В качестве боковой панели, мистер Денни абсолютно на ... постоянные числа правильных размеров или таблица Тали - это способ для большинства вещей. Что означает правильный размер? Ну, большинство людей используют таблицу Tally для генерации дат или разбивки на VARCHAR (8000). Если вы создадите таблицу Tally из 11 000 строк с правильным кластеризованным индексом «N», у вас будет достаточно строк для создания дат на более чем 30 лет (я работаю с ипотечными кредитами довольно много, поэтому 30 лет - это ключевой показатель для меня ) и, конечно, достаточно, чтобы справиться с разделением VARCHAR (8000). Почему «правильный размер» так важен? Если таблица Tally используется часто, она легко помещается в кэш, что делает ее невероятно быстрой без особого давления на память.
И последнее, но не менее важное: все знают, что если вы создаете постоянную таблицу Tally, то не имеет значения, какой метод вы используете для ее построения, потому что 1) это будет сделано только один раз и 2) если это что-то вроде таблица из 11 000 строк, все методы будут работать "достаточно хорошо". Так почему же все мои сомнения по поводу того, какой метод использовать ???
Ответ заключается в том, что какой-то бедный парень / девчонка, который не знает ничего лучше и просто нуждается в выполнении своей работы, может увидеть что-то вроде метода рекурсивного CTE и решит использовать его для чего-то гораздо большего и гораздо чаще Я использовал, чем создание постоянной таблицы Tally, и я пытаюсь защитить тех людей, серверы, на которых работает их код, и компанию, которой принадлежат данные на этих серверах . Да ... это так важно. Так должно быть и для всех остальных. Научите правильно делать вещи, а не «достаточно хорошо». Проведите некоторое тестирование, прежде чем публиковать или использовать что-то из поста или книги ... жизнь, которую вы спасете, может, на самом деле, быть вашей, особенно если вы думаете, что рекурсивный CTE - это путь для чего-то подобного. ; -)
Спасибо за прослушивание ...