我有一个递归函数,该函数使我可以在层次结构中提供任何GUID,并拉回其下的所有值。这用于文件夹安全。
ALTER FUNCTION dbo.ValidSiteClass
(
@GUID UNIQUEIDENTIFIER
)
RETURNS TABLE
AS
RETURN
(
-- Add the SELECT statement with parameter references here
WITH previous
AS ( SELECT
PK_SiteClass,
FK_Species_SiteClass,
CK_ParentClass,
ClassID,
ClassName,
Description,
SyncKey,
SyncState
FROM
dbo.SiteClass
WHERE
PK_SiteClass = @GUID
UNION ALL
SELECT
Cur.PK_SiteClass,
Cur.FK_Species_SiteClass,
Cur.CK_ParentClass,
Cur.ClassID,
Cur.ClassName,
Cur.Description,
Cur.SyncKey,
Cur.SyncState
FROM
dbo.SiteClass Cur,
previous
WHERE
Cur.CK_ParentClass = previous.PK_SiteClass)
SELECT DISTINCT
previous.PK_SiteClass,
previous.FK_Species_SiteClass,
previous.CK_ParentClass,
previous.ClassID,
previous.ClassName,
previous.Description,
previous.SyncKey,
previous.syncState
FROM
previous
)
我有一个存储的程序,以后需要确定用户的层次结构中哪些文件夹已更改,我将其用于更改跟踪。当我尝试通过更改跟踪将其加入时,它永远不会返回查询。例如,以下代码永远不会返回任何结果(它只会旋转,我会在6分钟后将其停止)
DECLARE @ChangeTrackerNumber INT = 13;
DECLARE @SelectedSchema UNIQUEIDENTIFIER = '36EC6589-8297-4A82-86C3-E6AAECCC7D95';
WITH validones AS (SELECT PK_SITECLASS FROM ValidSiteClass(@SelectedSchema))
SELECT SiteClass.PK_SiteClass KeyGuid,
'' KeyString,
dbo.GetChangeOperationEnum(SYS_CHANGE_OPERATION) ChangeOp
FROM dbo.SiteClass
INNER JOIN CHANGETABLE(CHANGES SiteClass, @ChangeTrackerNumber) tracking --tracking
ON tracking.PK_SiteClass = SiteClass.PK_SiteClass
INNER JOIN validones
ON SiteClass.PK_SiteClass = validones.PK_SiteClass
WHERE SyncState IN ( 0, 2, 4 );
我要做这项工作的唯一方法是使用诸如以下的模板:
DECLARE @ChangeTrackerNumber INT = 13;
DECLARE @SelectedSchema UNIQUEIDENTIFIER = '36EC6589-8297-4A82-86C3-E6AAECCC7D95';
CREATE TABLE #temptable
(
[PK_SiteClass] UNIQUEIDENTIFIER
);
INSERT INTO #temptable
(
PK_SiteClass
)
SELECT PK_SiteClass
FROM dbo.ValidSiteClass(@SelectedSchema);
SELECT SiteClass.PK_SiteClass KeyGuid,
'' KeyString,
dbo.GetChangeOperationEnum(SYS_CHANGE_OPERATION) ChangeOp
FROM dbo.SiteClass
INNER JOIN CHANGETABLE(CHANGES SiteClass, @ChangeTrackerNumber) tracking --tracking
ON tracking.PK_SiteClass = SiteClass.PK_SiteClass
INNER JOIN #temptable
ON SiteClass.PK_SiteClass = #temptable.PK_SiteClass
WHERE SyncState IN ( 0, 2, 4 );
DROP TABLE #temptable;
换句话说,CTE无法正常工作,我需要打电话给临时表。
第一个问题,CTE难道不应该比模板更好吗?
第二个问题,有人知道为什么会这样吗?我尝试了内部联接,也使用where和in子句。递归查询有什么不同之处,可能导致这种奇怪的行为?
答案 0 :(得分:1)
通常,当您具有表值函数时,只需像将其作为常规表一样包含它即可(假设您有要传递给它的参数)。如果要向其传递一系列参数,则可以使用outer apply
,但这里似乎并非如此。
我认为(也许)这更像您想要的(注意没有with
子句):
select
s.PK_SiteClass KeyGuid,
'' KeyString,
dbo.GetChangeOperationEnum(t.SYS_CHANGE_OPERATION) ChangeOp
from
dbo.ValidSiteClass(@SelectedSchema) v
inner join
SiteClass s
on
s.PK_SiteClass = v.PK_SiteClass
inner join
changetable(changes SiteClass, @ChangeTrackerNumber) c
on
c.PK_SiteClass = s.PK_SiteClass
where
SyncState in ( 0, 2, 4 )
option (force order)
...我承认,这看起来与您在with
子句中的使用在机械上没有什么不同。但是,您可能会遇到SQL Server问题,只是选择一个没有其他线索的可怕计划。包括option (force order)
可使SQL Server根据您放入连接的顺序执行连接。有时, 会产生不可思议的变化。
我不会建议这样做。实际上,这只是一个黑客……只是为了看一下WTF。但是,按顺序操作...并让SQL Server向您展示实际的执行计划,以了解为什么它可能会提出如此令人发指的事情。内联表值函数对SQL Server的查询计划引擎可见,并且它可能决定不按程序员传统上认为函数的方式将函数视为孤立的事物。我怀疑这就是为什么花这么长时间开始的原因。
有趣的是,如果您的函数是所谓的多行表值函数,那么在计划此查询时,SQL肯定不是具有相同的可见性类型...它可能会运行得更快。再次,不是建议,只是一些可能会制定更好计划的东西。