En mi publicación anterior, hablé sobre formas de generar una secuencia de números contiguos del 1 al 1000. Ahora me gustaría hablar sobre los siguientes niveles de escala:conjuntos generadores de 50 000 y 1 000 000 de números.
Generar un conjunto de 50.000 números
Cuando comencé esta serie, tenía mucha curiosidad sobre cómo los diferentes enfoques se escalarían a conjuntos de números más grandes. En el extremo inferior, estaba un poco consternado al descubrir que mi enfoque favorito:usar sys.all_objects
– no era el método más eficiente. Pero, ¿cómo se escalarían estas diferentes técnicas a 50 000 filas?
Tabla de números
Dado que ya hemos creado una tabla de Números con 1 000 000 de filas, esta consulta sigue siendo prácticamente idéntica:
SELECT TOP (50000) n FROM dbo.Numbers ORDER BY n;
Plano:
valores_spt
Dado que solo hay ~2500 filas en spt_values
, necesitamos ser un poco más creativos si queremos usarlo como fuente de nuestro generador de conjuntos. Una forma de simular una tabla más grande es CROSS JOIN
ella contra sí misma. Si lo hiciéramos sin procesar, terminaríamos con ~2500 filas al cuadrado (más de 6 millones). Al necesitar solo 50,000 filas, necesitamos alrededor de 224 filas al cuadrado. Entonces podemos hacer esto:
;WITH x AS ( SELECT TOP (224) number FROM [master]..spt_values ) SELECT TOP (50000) n = ROW_NUMBER() OVER (ORDER BY x.number) FROM x CROSS JOIN x AS y ORDER BY n;
Tenga en cuenta que esto es equivalente a, pero más conciso que, esta variación:
SELECT TOP (50000) n = ROW_NUMBER() OVER (ORDER BY x.number) FROM (SELECT TOP (224) number FROM [master]..spt_values) AS x CROSS JOIN (SELECT TOP (224) number FROM [master]..spt_values) AS y ORDER BY n;
En ambos casos, el plan se ve así:
sys.todos_los_objetos
Me gusta spt_values
, sys.all_objects
no satisface por sí solo nuestro requisito de 50 000 filas, por lo que tendremos que realizar una CROSS JOIN
similar .
;;WITH x AS ( SELECT TOP (224) [object_id] FROM sys.all_objects ) SELECT TOP (50000) n = ROW_NUMBER() OVER (ORDER BY x.[object_id]) FROM x CROSS JOIN x AS y ORDER BY n;
Plano:
CTE apilados
Solo necesitamos hacer un ajuste menor a nuestros CTE apilados para obtener exactamente 50 000 filas:
;WITH e1(n) AS ( 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 CROSS JOIN e1 AS b), -- 10*10 e3(n) AS (SELECT 1 FROM e2 CROSS JOIN e2 AS b), -- 100*100 e4(n) AS (SELECT 1 FROM e3 CROSS JOIN (SELECT TOP 5 n FROM e1) AS b) -- 5*10000 SELECT n = ROW_NUMBER() OVER (ORDER BY n) FROM e4 ORDER BY n;
Plano:
CTEs recursivos
Se requiere un cambio aún menos sustancial para obtener 50,000 filas de nuestro CTE recursivo:cambie el WHERE
cláusula a 50,000 y cambie el MAXRECURSION
opción a cero.
;WITH n(n) AS ( SELECT 1 UNION ALL SELECT n+1 FROM n WHERE n < 50000 ) SELECT n FROM n ORDER BY n OPTION (MAXRECURSION 0);
Plano:
En este caso, hay un ícono de advertencia en la ordenación; resulta que, en mi sistema, la ordenación necesitaba pasar a tempdb. Es posible que no vea un derrame en su sistema, pero esto debería ser una advertencia sobre los recursos necesarios para esta técnica.
Rendimiento
Al igual que con el último conjunto de pruebas, compararemos cada técnica, incluida la tabla Numbers con una caché fría y caliente, y tanto comprimida como sin comprimir:
Tiempo de ejecución, en milisegundos, para generar 50 000 números contiguos
Para obtener una mejor visualización, eliminemos el CTE recursivo, que fue un perro total en esta prueba y que distorsiona los resultados:
Tiempo de ejecución, en milisegundos, para generar 50 000 números contiguos (excluyendo recursivos CET)
Con 1000 filas, la diferencia entre comprimido y sin comprimir era mínima, ya que la consulta solo necesitaba leer 8 y 9 páginas respectivamente. Con 50 000 filas, la brecha se amplía un poco:74 páginas frente a 113. Sin embargo, el costo total de descomprimir los datos parece superar los ahorros en E/S. Entonces, con 50 000 filas, una tabla de números sin comprimir parece ser el método más eficiente para derivar un conjunto contiguo, aunque, hay que admitirlo, la ventaja es marginal.
Generando un conjunto de 1,000,000 de números
Si bien no puedo imaginar muchos casos de uso en los que necesite un conjunto contiguo de números tan grandes, quería incluirlo para completarlo y porque hice algunas observaciones interesantes a esta escala.
Tabla de números
No hay sorpresas aquí, nuestra consulta ahora es:
SELECT TOP 1000000 n FROM dbo.Numbers ORDER BY n;
El TOP
no es estrictamente necesario, pero eso es solo porque sabemos que nuestra tabla Numbers y nuestra salida deseada tienen el mismo número de filas. El plan sigue siendo bastante similar a las pruebas anteriores:
valores_spt
Para obtener un CROSS JOIN
eso produce 1,000,000 filas, necesitamos tomar 1,000 filas al cuadrado:
;WITH x AS ( SELECT TOP (1000) number FROM [master]..spt_values ) SELECT n = ROW_NUMBER() OVER (ORDER BY x.number) FROM x CROSS JOIN x AS y ORDER BY n;>
Plano:
sys.todos_los_objetos
De nuevo, necesitamos el producto vectorial de 1000 filas:
;WITH x AS ( SELECT TOP (1000) [object_id] FROM sys.all_objects ) SELECT n = ROW_NUMBER() OVER (ORDER BY x.[object_id]) FROM x CROSS JOIN x AS y ORDER BY n;
Plano:
CTE apilados
Para el CTE apilado, solo necesitamos una combinación ligeramente diferente de CROSS JOIN
s para llegar a 1 000 000 de filas:
;WITH e1(n) AS ( 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 CROSS JOIN e1 AS b), -- 10*10 e3(n) AS (SELECT 1 FROM e1 CROSS JOIN e2 AS b), -- 10*100 e4(n) AS (SELECT 1 FROM e3 CROSS JOIN e3 AS b) -- 1000*1000 SELECT n = ROW_NUMBER() OVER (ORDER BY n) FROM e4 ORDER BY n;
Plano:
Con este tamaño de fila, puede ver que la solución CTE apilada va en paralelo. Así que también ejecuté una versión con MAXDOP 1
para obtener una forma de plan similar a la anterior y para ver si el paralelismo realmente ayuda:
CTE recursivo
El CTE recursivo nuevamente tiene solo un cambio menor; solo el WHERE
la cláusula debe cambiar:
;WITH n(n) AS ( SELECT 1 UNION ALL SELECT n+1 FROM n WHERE n < 1000000 ) SELECT n FROM n ORDER BY n OPTION (MAXRECURSION 0);
Plano:
Rendimiento
Una vez más vemos que el rendimiento del CTE recursivo es abismal:
Tiempo de ejecución, en milisegundos, para generar 1 000 000 de números contiguos
Al eliminar ese valor atípico del gráfico, obtenemos una mejor imagen del rendimiento:
Tiempo de ejecución, en milisegundos, para generar 1 000 000 números contiguos (excluyendo recursivos CET)
Si bien nuevamente vemos la tabla Numbers sin comprimir (al menos con un caché tibio) como la ganadora, la diferencia, incluso a esta escala, no es tan notable.
Continuará...
Ahora que hemos explorado a fondo un puñado de enfoques para generar una secuencia de números, pasaremos a las fechas. En la publicación final de esta serie, veremos la construcción de un rango de fechas como un conjunto, incluido el uso de una tabla de calendario y algunos casos de uso en los que esto puede ser útil.
[ Parte 1 | Parte 2 | Parte 3 ]
Apéndice:Recuento de filas
Es posible que no esté intentando generar un número exacto de filas; en cambio, es posible que solo desee una forma sencilla de generar muchas filas. La siguiente es una lista de combinaciones de vistas de catálogo que le darán varios recuentos de filas si simplemente SELECT
sin un WHERE
cláusula. Tenga en cuenta que estos números dependerán de si se encuentra en un RTM o en un Service Pack (ya que algunos objetos del sistema se agregan o modifican) y también si tiene una base de datos vacía.
Fuente | Número de filas | ||
---|---|---|---|
Servidor SQL 2008 R2 | Servidor SQL 2012 | Servidor SQL 2014 | |
maestro..spt_valores | 2508 | 2515 | 2519 |
maestro..valores_spt CROSS JOIN maestro..valores_spt | 6,290,064 | 6,325,225 | 6,345,361 |
sys.todos_los_objetos | 1990 | 2089 | 2165 |
sys.todas_las_columnas | 5157 | 7276 | 8560 |
sys.all_objects CROSS JOIN sys.all_objects | 3.960.100 | 4.363.921 | 4.687.225 |
sys.all_objects CROSS JOIN sys.all_columns | 10,262,430 | 15,199,564 | 18,532,400 |
sys.all_columns CROSS JOIN sys.all_columns | 26 594 649 | 52,940,176 | 73,273,600 |
Tabla 1:Recuentos de filas para varias consultas de vista de catálogo