2011-09-15 5 views
31

estoy frente a un problema de declarar la opción MAXRECURSION para un CTE dentro de un TVF¿Cómo configurar la opción MAXRECURSION para un CTE dentro de una tabla con valores de funciones

aquí es el CTE (un simple calendario):

DECLARE @DEBUT DATE = '1/1/11', @FIN DATE = '1/10/11'; 

WITH CTE as(  
SELECT @debut as jour  
UNION ALL  
SELECT DATEADD(day, 1, jour)  
FROM CTE  
WHERE DATEADD(day, 1, jour) <= @fin) 
SELECT jour FROM CTE option (maxrecursion 365) 

y el TVF:

CREATE FUNCTION [liste_jour] 
(@debut date,@fin date) 
RETURNS TABLE 
AS  
RETURN  
( 
    WITH CTE as(  
    SELECT @debut as jour  
    UNION ALL  
    SELECT DATEADD(day, 1, jour)  
    FROM CTE  
    WHERE DATEADD(day, 1, jour) <= @fin) 
    SELECT jour FROM CTE 
    --option (maxrecursion 365) 
) 

lo anterior TVF está funcionando bien sin la opción MAXRECURSION pero hay un error de sintaxis con la opción. ¿cuál es la solución?

respecto

Respuesta

37

Desde this MSDN forums thread me entero de que

[la] OPTION cláusula sólo se puede utilizar a nivel de estado

Así que no se puede utilizar dentro de una expresión de consulta dentro de las definiciones de vista o TVF en línea, etc. La única forma de usarlo en su caso es crear el TVF sin la cláusula OPTION y especificarlo en la consulta que usa el TVF. Tenemos un error que rastrea la solicitud para permitir el uso de la cláusula OPTION dentro de cualquier expresión de consulta (por ejemplo, if exists() o CTE o ver).

y más

No se puede cambiar el valor predeterminado de esta opción dentro de una UDF. Usted tendrá que hacerlo en la declaración que hace referencia al udf.

Así que en su ejemplo, debe especificar la llamada OPTION cuando su función:

CREATE FUNCTION [liste_jour] 
(@debut date,@fin date) 
RETURNS TABLE 
AS  
RETURN  
( 
    WITH CTE as(  
    SELECT @debut as jour  
    UNION ALL  
    SELECT DATEADD(day, 1, jour)  
    FROM CTE  
    WHERE DATEADD(day, 1, jour) <= @fin) 
    SELECT jour FROM CTE -- no OPTION here 
) 

(más adelante)

SELECT * FROM [liste_jour] (@from , @to) OPTION (MAXRECURSION 365) 

Tenga en cuenta que no se puede trabajar alrededor de este al tener un segundo TVF que solo hace la línea anterior, obtienes el mismo error, si lo intentas. "[la] cláusula OPTION se puede usar solo en el nivel de declaración", y eso es definitivo (por ahora).

22

hilo viejo, no sé, pero necesitaba lo mismo y solo se ocuparon de ello mediante el uso de una UDF instrucción múltiple:

CREATE FUNCTION DatesInRange 
(
    @DateFrom datetime, 
    @DateTo datetime 
) 
RETURNS 
@ReturnVal TABLE 
(
    date datetime 
) 
AS 
BEGIN 

    with DateTable as (
     select dateFrom = @DateFrom 

     union all 

     select DateAdd(day, 1, df.dateFrom) 
     from DateTable df 
     where df.dateFrom < @DateTo 
    ) 
    insert into @ReturnVal(date) 

    select dateFrom 

    from DateTable option (maxrecursion 32767) 

    RETURN 
END 
GO 

Probablemente hay problemas de eficiencia con esto, pero me lo puedo permitir, en mi caso.

+3

Esta respuesta puede ser un poco mejor, porque no hay necesidad de recordar acerca de CTE (sin límite de recursividad) en una función. –

+0

Por supuesto, resuelve el problema, pero tiene "posiblemente un inconveniente", porque esta función con valores de tabla es 'no en línea '. Puede ser crusial en términos de rendimiento. – pkuderov

+0

Como dije, "Probablemente haya problemas de eficiencia con esto, pero puedo pagarlo en mi caso". – Crisfole

2

Otra forma de resolver esto es dividir el problema en un par de CTE, ninguno de los cuales alcanza el límite de recursión de 100. El primer CTE crea una lista con la fecha de inicio de cada mes en el rango. El segundo CTE se completa todos los días de cada mes. Siempre que el rango de entrada sea inferior a 100 meses, debería funcionar bien. Si se requiere un rango de entrada de más de 100 meses, la misma idea podría expandirse con un tercer CTE por años agregado antes de los meses CTE.

CREATE FUNCTION [liste_jour]  
(@debut datetime, @fin datetime)  
RETURNS TABLE 
AS  
RETURN   
( 
    WITH CTE_MOIS AS 
    (   
     SELECT JOUR_DEBUT = @debut 
     UNION ALL 
     SELECT DATEADD(MONTH, 1, CTE_MOIS.JOUR_DEBUT) 
      FROM CTE_MOIS   
     WHERE DATEADD(MONTH, 1, CTE_MOIS.JOUR_DEBUT) <= @fin 
    ), 

    CTE_JOUR AS 
    (   
     SELECT JOUR = CTE_MOIS.JOUR_DEBUT 
      FROM CTE_MOIS 
     UNION ALL   
     SELECT DATEADD(DAY, 1, CTE_JOUR.JOUR) 
      FROM CTE_JOUR 
     WHERE MONTH(CTE_JOUR.JOUR) = MONTH(DATEADD(DAY, 1, CTE_JOUR.JOUR)) AND 
      DATEADD(DAY, 1, CTE_JOUR.JOUR) <= @FIN 
    ) 

    SELECT JOUR 
     FROM CTE_JOUR 
) 
2

tema antiguo, pero ... Sólo quería aclarar por qué OPCIÓN (MAXRECURSION x) no está permitido en una función con valores de tabla en línea. Esto se debe a que el de iTVF se ingresa en cuando los utiliza en una consulta. Y, como todos sabemos, no puede colocar esta opción en ningún otro lugar excepto al final de la consulta. Esto es EL razón por la que nunca será posible ponerlo dentro de un iTVF (a menos que el analizador sintáctico y/o el algebrizador hagan algo de magia detrás de las escenas, lo que no creo que vaya a suceder pronto). mTVF (funciones con valores de tabla de varias declaraciones) es una historia diferente porque no se incluyen (y son tan lentas que nunca se deben usar en las consultas; no obstante, está bien usarlas en una asignación a una variable, pero entonces otra vez --- ¡cuidado con los bucles!).

0

Un poco de uso creativo de CET y productos cartesianos (combinaciones cruzadas) lo acercará al límite MAXRECURSION de 100. 3 CTE con un límite de 4 registros en el último le proporciona 40,000 registros, lo que será bueno para más de 100 años de datos. Si espera más diferencia entre @debut y @fin, puede ajustar cte3. Además, por favor, deje de GRITAR su SQL.

-- please don't SHOUTCASE your SQL anymore... this ain't COBOL 
alter function liste_jour(@debut date, @fin date) returns table as 
return ( 
    with cte as (
     select 0 as seq1 
     union all 
     select seq1 + 1 
     from cte 
     where seq1 + 1 < 100 
    ), 
    cte2 as (
     select 0 as seq2 
     union all 
     select seq2 + 1 
     from cte2 
     where seq2 + 1 < 100 
    ), 
    cte3 as (
     select 0 as seq3 
     union all 
     select seq3 + 1 
     from cte3 
     where seq3 + 1 <= 3 -- increase if 100 years isn't good enough 
    ) 
    select 
     dateadd(day, (seq1 + (100 * seq2) + (10000 * seq3)), @debut) as jour 
    from cte, cte2, cte3 
    where (seq1 + (100 * seq2) + (10000 * seq3)) <= datediff(day, @debut, @fin) 
) 
go 
-- test it! 
select * from liste_jour('1/1/2000', '2/1/2000') 
Cuestiones relacionadas