SQL SELECT Convertir Min/Max en filas separadas
Tengo una tabla que tiene un valor mínimo y máximo que me gustaría crear una fila para cada número válido en una instrucción SELECT.
Tabla original:
| Foobar_ID | Min_Period | Max_Period |
---------------------------------------
| 1 | 0 | 2 |
| 2 | 1 | 4 |
Me gustaría convertir eso en:
| Foobar_ID | Period_Num |
--------------------------
| 1 | 0 |
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 2 | 4 |
Los resultados de SELECT deben aparecer como un conjunto de resultados, por lo que no estoy seguro de si un bucle WHILE
funcionaría en mi caso.
Mostrar la mejor respuesta
Publicado : 4 April, 2018 @ 15:23
¿Tienes una tabla de hechos de números?
Publicado : 4 April, 2018 @ 15:25
Únete a una tabla con números: ON n between Min_Period and Max_Period
Publicado : 4 April, 2018 @ 15:25
Publicado : 4 April, 2018 @ 15:40
La pregunta es clara y no es necesario votar negativamente, +1
Publicado : 4 April, 2018 @ 15:49
Si espera solo un puñado de filas por foobar, entonces esta es una buena oportunidad para aprender sobre CTE recursivos:
with cte as (
select foobar_id, min_period as period_num, max_period
from original t
union all
select foobar_id, min_period + 1 as period_num, max_period
from cte
where period_num < max_period
)
select foobar_id, period_num
from cte
order by foobar_id, period_num;
Puede extender esto a cualquier cantidad de períodos configurando la opción MAXRECURSION
en 0.
Publicado : 4 April, 2018 @ 15:25
¿Puedes editar los nombres de las tablas? El nombre de CTE es t
pero está seleccionando de cte
. Sería genial si también pudiera agregar la opción MAXRECURSION
para que el OP sepa dónde editar.
Publicado : 4 April, 2018 @ 15:30
Mi rango de períodos es de 0 a 11, así que supongo que simplemente repetir la instrucción SELECT
funcionaría, pero esperaba una solución más dinámica. Trabajaré en este enfoque por ahora. Gracias
Publicado : 4 April, 2018 @ 15:31
Esta respuesta es la que necesita @Panman, es un CTE recursivo que generará todos los registros de una sola vez.
Publicado : 4 April, 2018 @ 15:31
Sí, CTE recursivo funciona muy bien. Después de revisar esta sugerencia más de cerca, conseguí que funcionara según lo previsto. ¡Gracias de nuevo!
Publicado : 4 April, 2018 @ 16:02
Un método sería usar una tabla Tally, hay muchos ejemplos, pero voy a crear uno muy pequeño en este ejemplo. Luego puede JOIN
sobre eso y devolver su conjunto de resultados.
--Create the Tally Table
CREATE TABLE #Tally (I int);
WITH ints AS(
SELECT 0 AS i
UNION ALL
SELECT i + 1
FROM ints
WHERE i + 1 <= 10)
--And in the numbers go!
INSERT INTO #Tally
SELECT i
FROM ints;
GO
--Create the sample table
CREATE TABLE #Sample (ID int IDENTITY(1,1),
MinP int,
MaxP int);
--Sample data
INSERT INTO #Sample (Minp, MaxP)
VALUES (0,2),
(1,4);
GO
--And the solution
SELECT S.ID,
T.I AS P
FROM #Sample S
JOIN #Tally T ON T.I BETWEEN S.MinP AND S.MaxP
ORDER BY S.ID, T.I;
GO
--Clean up
DROP TABLE #Sample;
DROP TABLE #Tally;
Publicado : 4 April, 2018 @ 15:28
Gracias por la sugerencia, después de revisar los "CTE recursivos", pude hacer todo en un CTE.
Publicado : 4 April, 2018 @ 16:01
Dependiendo del tamaño de los datos y el rango del período, la forma más fácil de hacer esto es usar una tabla de hechos de números dinámicos, de la siguiente manera:
WITH rn AS (SELECT ROW_NUMBER() OVER (ORDER BY object_id) -1 as period_num FROM sys.objects)
SELECT f.foobar_id, rn.period_num
FROM foobar f
INNER JOIN rn ON rn.period_num BETWEEN f.min_period AND f.max_period
Sin embargo, si está trabajando con un mayor volumen de datos, valdrá la pena crear una tabla de hechos numéricos con un índice. Incluso puedes usar un TVV para esto:
-- Declare the number fact table
DECLARE @rn TABLE (period_num INT IDENTITY(0, 1) primary key, dummy int)
-- Populate the fact table so that all periods are covered
WHILE (SELECT COUNT(1) FROM @rn) < (SELECT MAX(max_period) FROM foobar)
INSERT @rn select 1 from sys.objects
-- Select using a join to the fact table
SELECT f.foo_id, rn.period_num
FROM foobar f
inner join @rn rn on rn.period_num between f.min_period and f.max_period
Publicado : 4 April, 2018 @ 15:59
Simplemente cree una fecha de muestra de función y utilícela
CREATE FUNCTION [dbo].[Ufn_GetMInToMaxVal] (@Min_Period INT,@Max_Period INT )
RETURNS @OutTable TABLE
(
DATA INT
)
AS
BEGIN
;WIth cte
AS
(
SELECT @Min_Period As Min_Period
UNION ALL
SELECT Min_Period+1 FRom
cte
WHERE Min_Period < @Max_Period
)
INSERT INTO @OutTable
SELECT * FROM cte
RETURN
END
Obtenga el resultado ejecutando la instrucción sql
DECLARE @Temp AS TABLE(
Foobar_ID INT,
Min_Period INT,
Max_Period INT
)
INSERT INTO @Temp
SELECT 1, 0,2 UNION ALL
SELECT 2, 1,4
SELECT Foobar_ID ,
DATA
FROM @Temp
CROSS APPLY
[dbo].[Ufn_GetMInToMaxVal] (Min_Period,Max_Period)
Resultado
Foobar_ID DATA
----------------
1 0
1 1
1 2
2 1
2 2
2 3
2 4
Publicado : 10 April, 2018 @ 12:54