Рекурсивная функция SQL и отслеживание изменений не работают вместе - PullRequest
1 голос
/ 29 марта 2019

У меня есть рекурсивная функция, которая позволяет мне задавать любой 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 не работает, и мне нужно вызвать temptable.

Первый вопрос, разве CTE не должен быть тем же (но лучше), чем искушаемый?

Второй вопрос, кто-нибудь знает, почему это может быть так? Я пробовал внутренние соединения и с помощью оператора where и in. Есть ли что-то другое в рекурсивном запросе, который может вызвать это странное поведение?

1 Ответ

1 голос
/ 29 марта 2019

Обычно, когда у вас есть табличная функция, вы просто включаете ее, как обычную таблицу (при условии, что у вас есть параметр для передачи ей).Если вы хотите передать ему ряд параметров, вы должны использовать 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 определенно не иметь такой же видимость при планировании этого запроса ... и он может работать быстрее.Опять же, не рекомендация, просто что-то, что может взломать лучший план.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...