Ответ 1
Хе-хе... извините, я так поздно отвечаю на старый пост. И, да, я должен был ответить, потому что самый популярный ответ (в то время, рекурсивный ответ CTE со ссылкой на 14 разных методов) в этом потоке, ummm... производительность поставлена в лучшем случае.
Во-первых, статья с 14 различными решениями отлично подходит для просмотра различных методов создания таблицы Numbers/Tally на лету, но, как указано в статье и в цитированной ветке, есть очень важная цитата...
", касающиеся эффективности и производительность часто субъективна. Независимо от того, как выполняется запрос используется, физическая реализация определяет эффективность запроса. Поэтому вместо того, чтобы полагаться на предвзятые рекомендации, необходимо что вы проверяете запрос и определяете который лучше работает".
По иронии судьбы, сама статья содержит множество субъективных утверждений и "предвзятые рекомендации", такие как "рекурсивный CTE может генерировать числовое перечисление довольно эффективно" и "Это эффективный метод использования цикла WHILE из публикации новостей группы Itzik Ben-Gen" (который, я уверен, он опубликовал только для целей сравнения). C'mon люди... Просто упомянуть хорошее имя Итзика, может привести к тому, что какой-нибудь бедный slob на самом деле использует этот ужасный метод. Автор должен практиковать то, что он проповедует, и должен сделать небольшое тестирование производительности, прежде чем делать такие смехотворно неправильные утверждения, особенно перед лицом любой масштабируемости.
С мыслью о том, чтобы действительно провести некоторое тестирование, прежде чем принимать какие-либо субъективные претензии о том, что делает какой-либо код или что кому-то нравится, вот какой код вы можете провести самостоятельно. Установите профайлер для 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 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 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 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 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 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 CROSS JOINED CTE method 1448 1328 1217 2095
51 --===== Housekeeping DROP TABLE #Tally 8 0 415 0
Как вы можете видеть, рекурсивный метод CTE является вторым худшим только для цикла While Loop for Duration и CPU и имеет 8-кратное значение давления памяти в виде логических чтений, чем цикл While Loop. Это RBAR на стероидах, и его следует избегать любой ценой для любых вычислений в одной строке, так как следует избегать цикла While Loop. Есть места, где рекурсия весьма ценна, но это НЕ один из них.
Как боковая панель, г-н Денни абсолютно спокоен... правильная по размеру постоянная таблица Numbers или Tally - это путь для большинства вещей. Что означает правильный размер? Ну, большинство людей используют таблицу Tally для создания дат или для разделения на VARCHAR (8000). Если вы создадите таблицу Tally из 11 000 строк с правильным кластеризованным индексом на "N", у вас будет достаточно строк для создания дат за 30 лет (я работаю с ипотечными кредитами, так что 30 лет - это ключевой номер для меня ) и, конечно, достаточно для обработки VARCHAR (8000) раскола. Почему так важна "правильная калибровка"? Если таблица Tally используется много, она легко вписывается в кеш, что делает ее невероятно быстрой без особого давления на память.
И последнее, но не менее важное: каждый знает, что если вы создаете постоянную таблицу Tally, не имеет большого значения, какой метод вы используете для ее создания, потому что 1) она будет сделана только один раз и 2), если это что-то вроде таблица из 11 000 строк, все методы будут работать "достаточно хорошо". Итак, почему все различия в моей части о том, какой метод использовать???
Ответ заключается в том, что какой-то бедный парень/парень, который не знает ничего лучшего и просто должен выполнить свою работу, может увидеть что-то вроде метода рекурсивного CTE и решил использовать его для чего-то гораздо большего и гораздо более частого используется для создания постоянной таблицы Tally, и я пытаюсь защитить тех людей, серверов, на которых работает их код, и компании, которая владеет данными на этих серверах. Да... это такая большая сделка. Это должно быть и для всех остальных. Учите правильный способ делать что-то, а не "достаточно хорошо". Проведите некоторое тестирование перед публикацией или использованием чего-либо из сообщения или книги... жизнь, которую вы сохраняете, может быть, по сути, вашей собственной, особенно если вы считаете, что рекурсивный CTE - это способ пойти на что-то подобное.; -)
Спасибо за прослушивание...