Объединение динамических таблиц в один запрос

#sql #sql-server #sql-optimization

#sql #sql-сервер #sql-оптимизация

Вопрос:

Есть ли простой способ объединить приведенный ниже SQL в 1 инструкцию? Идентификатор фильтра может иметь нулевую длину, поэтому Filters таблица является необязательной. Итак, если нет лучшего способа сделать это, я «вынужден» использовать приведенную ниже логику EXISTS.

Я уже понимаю…
Я уже понимаю, что могу создать динамическую строку и использовать EXEC для ее запуска … но это медленно.

Отношение Document-to-DocumentMetadata равно: 1 Document ко многим DocumentMetadata
Отношение Filter-to-DocumentMetadata равно: 1 ко многим Filter DocumentMetadata

SQL выглядит следующим образом:

    DECLARE @SearchTerms NVARCHAR(MAX)
    DECLARE @FilterIDs  VARCHAR(100)
    SET @SearchTerms        = '7%'
    SET @FilterIDs      = '12'
    ------------------------
    DECLARE @Filters TABLE (FilterID int)

    IF(@FilterIDs IS NOT NULL)
            INSERT INTO @Filters SELECT items AS INT FROM Split(@filterIDs, ',')

    IF EXISTS(SELECT FilterID FROM @Filters)
        BEGIN
            SELECT
                Document.ID AS DocumentID
                ,Document.SourceID
                ,Document.Name
                ,Document.Title
                ,Document.DocumentUrl
                ,Document.DocType
                ,Document.DocumentModifiedDate
                ,Document.Library
                ,DocumentMetadata.ID AS DocumentMetadataID
                ,DocumentMetadata.DocumentID
                ,DocumentMetadata.FilterID
                ,DocumentMetadata.Value
            FROM Document
            JOIN DocumentMetadata
                ON DocumentMetadata.DocumentID = Document.ID
            JOIN Filter
                ON Filter.ID = DocumentMetadata.FilterID
                AND Filter.ID IN (SELECT FilterID FROM @Filters)
            WHERE
                Document.Name LIKE @SearchTerms
                OR Document.Title LIKE @SearchTerms
            ORDER BY
                Document.Name, Document.Title
        END
    ELSE
        BEGIN
            SELECT
                Document.ID AS DocumentID
                ,Document.SourceID
                ,Document.Name
                ,Document.Title
                ,Document.DocumentUrl
                ,Document.DocType
                ,Document.DocumentModifiedDate
                ,Document.Library
                ,DocumentMetadata.ID AS DocumentMetadataID
                ,DocumentMetadata.DocumentID
                ,DocumentMetadata.FilterID
                ,DocumentMetadata.Value
            FROM Document
            JOIN DocumentMetadata
                ON DocumentMetadata.DocumentID = Document.ID
            JOIN Filter
                ON Filter.ID = DocumentMetadata.FilterID
            WHERE
                Document.Name LIKE @SearchTerms
                OR Document.Title LIKE @SearchTerms
            ORDER BY
                Document.Name, Document.Title
        END
  

Ответ №1:

 JOIN Filter ON Filter.ID = DocumentMetadata.FilterID 
AND (Filter.ID IN (SELECT FilterID FROM @Filters) OR @FilterIDs is null) 
  

Это должно касаться того, есть ли у вас элементы в таблице @filters или у вас было введено значение null для filtersID (которое управляет таблицей Filters). В случае, если в предложении OR указано значение null, запись пропускается, поэтому она полагается на другую часть предложения join, которая является общей для ваших операторов. Это должно позволить вам просто использовать 1 предложение select для покрытия обоих.

Комментарии:

1. Обратите внимание, что этот метод может снизить производительность на больших наборах данных; для таблиц меньшего размера это нормально (anc, безусловно, проще в обслуживании).

2. @Stuart У тебя есть другой способ?

3. Если количество элементов в @Filters довольно велико, то это может пострадать, но при небольших числах все будет в порядке.

4. @Andrew Из любопытства, насколько высок «довольно высокий» … 5, 30, 100?

5. Я помню, что на SQL Server 2005 RTM поведение плана запроса изменилось в 64 элементах, позже это изменилось — лучший подход — запустить несколько тестов с вашими данными. Я бы также отметил, что целью было решить двойные операторы — использование IN для большого количества значений — это другой вопрос, и я почти уверен, что он поднимался на SO несколько раз раньше