递归SQL函数和更改跟踪不能一起使用

时间:2019-03-28 21:55:16

标签: sql sql-server

我有一个递归函数,该函数使我可以在层次结构中提供任何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子句。递归查询有什么不同之处,可能导致这种奇怪的行为?

1 个答案:

答案 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肯定不是具有相同的可见性类型...它可能会运行得更快。再次,不是建议,只是一些可能会制定更好计划的东西。