TVF内のCTEのmaxrecursionオプションを宣言する問題に直面しています。
CTE(シンプルなカレンダー)は次のとおりです。
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)
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)
)
上記のTVFはmaxrecursionオプションなしで正常に実行されていますが、オプションに構文エラーがあります。解決策は何ですか?
このMSDNフォーラムスレッド から学ぶ
[the]
OPTION
句はステートメントレベルでのみ使用できますしたがって、ビュー定義またはインラインTVFなどのクエリ式内で使用することはできません。あなたのケースで使用する唯一の方法は、
OPTION
句なしでTVFを作成し、TVFを使用するクエリで指定することです。クエリ式内でOPTION
句の使用を許可するリクエストを追跡するバグがあります(たとえば、if exists()
またはCTEまたはビュー)。
そしてさらに
Udf内でそのオプションのデフォルト値を変更することはできません。 udfを参照するステートメントで行う必要があります。
したがって、この例では、関数をcallするときにOPTION
を指定する必要があります。
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
)
(後)
SELECT * FROM [liste_jour] ( @from , @to ) OPTION ( MAXRECURSION 365 )
上記の行を実行するだけの2番目のTVFを使用することで、これを回避することはできません。同じエラーが発生すると、同じエラーが発生します。 「[the] OPTION
句はステートメントレベルでのみ使用できます」、そしてこれが最後です(今のところ)。
古いスレッド、私は知っていますが、私は同じことを必要としており、マルチステートメントUDFを使用してそれを処理しました:
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
これにはおそらく効率の問題がありますが、私の場合は余裕があります。
古い問題ですが...インラインテーブル値関数でOPTION(MAXRECURSION x)が許可されない理由を明確にしたかっただけです。これは、クエリでiTVFを使用する場合、iTVFのインライン化であるためです。そして、私たち全員が知っているように、このオプションをクエリの最後に保存する他の場所に置くことはできません。これは[〜#〜] the [〜#〜] iTVF内に配置できない理由です(パーサーや代数器が舞台裏で魔法をかけない限り、いつでもそうなるとは思わない)。 mTVF(複数ステートメントのテーブル値関数)はインライン化されないため(また、クエリで使用されるべきではないほど遅いため、変数への代入で使用しても構いませんが)、別の話です。そして再び---ループに注意してください!)。
これを処理するもう1つの方法は、問題を1組のCTEに分割することです。どちらも100の再帰制限に達しません。最初のCTEは、範囲内の各月の開始日を含むリストを作成します。次に、2番目のCTEが各月のすべての日を埋めます。入力範囲が100か月未満である限り、正常に機能するはずです。 100か月を超える入力範囲が必要な場合、CTEの数か月前に3年目の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
)
簡単なサンプルを作成してください:)
/ * sqlのテスト用のブロック作成関数*// * FUNCTION [fn_CTE_withLevel](@max_level int)RETURNS TABLE AS RETURN
(* /
/ *******************テーブルを宣言するだけで実際のテーブルを置き換えます***** /
declare @tbl table(pid varchar(15),id varchar(15))
/* use function argument */
declare @max_level int = 3
Insert Into @tbl(pid , id)
values
/*lev1*/ ('0','1') ,
/*lev2*/ ('1','101') ,
/*lev2*/ ('1','102') ,
/*lev1*/ ('0','2') ,
/*lev2*/ ('2','201') ,
/*lev3*/ ('201','20101') ,
/*lev3*/ ('201','20102') ,
/*lev2*/ ('2','202') ,
/*lev1*/ ('0','3') ,
/*lev2*/ ('3','301') ,
/*lev2*/ ('3','302') ,
/*lev1*/ ('0','4') ,
/*lev2*/ ('4','401'),
/*lev2*/ ('4','402');
/ *******************テーブルを宣言するだけで実際のテーブルを置き換えます***** /
With cte_result(pid , id , lev)
As(
Select pid , id , 1 as lev From @tbl t
Where pid = '0' /* change to another values from list to test sub items */
Union All
Select t.pid , t.id , cte.lev + 1 as lev
From cte_result cte
inner Join @tbl t
On cte.id = t.pid
Where cte.lev < @max_level -- :) this is my idea
)
Select * From cte_result
--OPTION (MAXRECURSION 100)
-作成関数のコメント解除/ ) /
CTEとデカルト積(クロスジョイン)を少し創造的に使用すると、100のMAXRECURSION
制限を回避することができます。 100年以上のデータ。 @debutと@finの違いが予想される場合は、cte3
。また、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')