Компилятор, кажется, оценивает каждый оператор if, даже если условия не выполняются в SQL

У меня хранимая процедура содержит чуть более 300000 строк, и я впервые работаю над чем-то такого размера. вот пример кода, который я пишу:

      IF (@type = 'commande client')
AND @etat = 'ouvert'
BEGIN
    IF (@tiersd = '')
   AND @tiersf = ''
    BEGIN
        IF (@familled = '')
       AND @famillef = ''
        BEGIN
            IF (@commerciald = '')
           AND @commercialf = ''
            BEGIN
                IF (@articled = '')
               AND @articlef = ''
                BEGIN
                    IF (@afamilled = '')
                   AND @afamillef = ''
                    BEGIN
                        SELECT ordr.docentry,
                               OITM.itemCode,
                               rdr1.dscription,
                               rdr1.quantity,
                               rdr1.price,
                               rdr1.currency,
                               rdr1.slpCode,
                               rdr1.basedocnum,
                               rdr1.shiptocode,
                               rdr1.shiptodesc,
                               rdr1.baseprice,
                               ordr.docnum,
                               ordr.doctype,
                               ordr.docstatus,
                               ordr.docTotal,
                               ordr.docdate,
                               ordr.cardcode,
                               ordr.cardname,
                               ordr.address,
                               ordr.doccur,
                               ordr.paidtodate,
                               ordr.doctime,
                               ordr.docsubtype,
                               ordr.basetype,
                               ordr.baseEntry,
                               OITM.itemclass,
                               OITM.itemtype,
                               OITM.itemname,
                               OITM.itmsgrpcod
                        FROM ordr
                             INNER JOIN rdr1 ON ordr.docentry = rdr1.docentry
                             INNER JOIN oitm ON OITM.itemcode = rdr1.ItemCode
                        WHERE ordr.DocDate BETWEEN @dated AND @datef
                          AND DocStatus = 'o';
                    END;
                    ELSE IF (@afamilled != '')
                        AND @afamillef = ''
                    BEGIN
                        SELECT ordr.docentry,
                               OITM.itemCode,
                               rdr1.dscription,
                               rdr1.quantity,
                               rdr1.price,
                               rdr1.currency,
                               rdr1.slpCode,
                               rdr1.basedocnum,
                               rdr1.shiptocode,
                               rdr1.shiptodesc,
                               rdr1.baseprice,
                               ordr.docnum,
                               ordr.doctype,
                               ordr.docstatus,
                               ordr.docTotal,
                               ordr.docdate,
                               ordr.cardcode,
                               ordr.cardname,
                               ordr.address,
                               ordr.doccur,
                               ordr.paidtodate,
                               ordr.doctime,
                               ordr.docsubtype,
                               ordr.basetype,
                               ordr.baseEntry,
                               OITM.itemclass,
                               OITM.itemtype,
                               OITM.itemname,
                               OITM.itmsgrpcod
                        FROM ordr
                             INNER JOIN rdr1 ON ordr.docentry = rdr1.docentry
                             INNER JOIN oitm ON OITM.itemcode = rdr1.ItemCode
                        WHERE OITM.itmsgrpcod BETWEEN @afamilled AND ((SELECT MAX(itmsgrpcod)FROM oitm))
                          AND ordr.DocDate BETWEEN @dated AND @datef
                          AND DocStatus = 'o';
                    END;

                    ELSE IF (@afamilled = '')
                        AND @afamillef != ''
                    BEGIN
                        SELECT ordr.docentry,
                               OITM.itemCode,
                               rdr1.dscription,
                               rdr1.quantity,
                               rdr1.price,
                               rdr1.currency,
                               rdr1.slpCode,
                               rdr1.basedocnum,
                               rdr1.shiptocode,
                               rdr1.shiptodesc,
                               rdr1.baseprice,
                               ordr.docnum,
                               ordr.doctype,
                               ordr.docstatus,
                               ordr.docTotal,
                               ordr.docdate,
                               ordr.cardcode,
                               ordr.cardname,
                               ordr.address,
                               ordr.doccur,
                               ordr.paidtodate,
                               ordr.doctime,
                               ordr.docsubtype,
                               ordr.basetype,
                               ordr.baseEntry,
                               OITM.itemclass,
                               OITM.itemtype,
                               OITM.itemname,
                               OITM.itmsgrpcod
                        FROM ordr
                             INNER JOIN rdr1 ON ordr.docentry = rdr1.docentry
                             INNER JOIN oitm ON OITM.itemcode = rdr1.ItemCode
                        WHERE OITM.itmsgrpcod BETWEEN ((SELECT MIN(itmsgrpcod)FROM oitm)) AND @afamillef
                          AND ordr.DocDate BETWEEN @dated AND @datef
                          AND DocStatus = 'o';
                    END;

                    IF (@afamilled != '')
                   AND @afamillef != ''
                    BEGIN
                        SELECT ordr.docentry,
                               OITM.itemCode,
                               rdr1.dscription,
                               rdr1.quantity,
                               rdr1.price,
                               rdr1.currency,
                               rdr1.slpCode,
                               rdr1.basedocnum,
                               rdr1.shiptocode,
                               rdr1.shiptodesc,
                               rdr1.baseprice,
                               ordr.docnum,
                               ordr.doctype,
                               ordr.docstatus,
                               ordr.docTotal,
                               ordr.docdate,
                               ordr.cardcode,
                               ordr.cardname,
                               ordr.address,
                               ordr.doccur,
                               ordr.paidtodate,
                               ordr.doctime,
                               ordr.docsubtype,
                               ordr.basetype,
                               ordr.baseEntry,
                               OITM.itemclass,
                               OITM.itemtype,
                               OITM.itemname,
                               OITM.itmsgrpcod
                        FROM ordr
                             INNER JOIN rdr1 ON ordr.docentry = rdr1.docentry
                             INNER JOIN oitm ON OITM.itemcode = rdr1.ItemCode
                        WHERE OITM.itmsgrpcod BETWEEN @afamilled AND @afamillef
                          AND ordr.DocDate BETWEEN @dated AND @datef
                          AND DocStatus = 'o';
                    END;
                END;

                ----------------------------

                ELSE IF (@articled != '')
                    AND @articlef != ''
                BEGIN
                    IF (@afamilled = '')
                   AND @afamillef = ''
                    BEGIN
                        SELECT ordr.docentry,
                               OITM.itemCode,
                               rdr1.dscription,
                               rdr1.quantity,
                               rdr1.price,
                               rdr1.currency,
                               rdr1.slpCode,
                               rdr1.basedocnum,
                               rdr1.shiptocode,
                               rdr1.shiptodesc,
                               rdr1.baseprice,
                               ordr.docnum,
                               ordr.doctype,
                               ordr.docstatus,
                               ordr.docTotal,
                               ordr.docdate,
                               ordr.cardcode,
                               ordr.cardname,
                               ordr.address,
                               ordr.doccur,
                               ordr.paidtodate,
                               ordr.doctime,
                               ordr.docsubtype,
                               ordr.basetype,
                               ordr.baseEntry,
                               OITM.itemclass,
                               OITM.itemtype,
                               OITM.itemname,
                               OITM.itmsgrpcod
                        FROM ordr
                             INNER JOIN rdr1 ON ordr.docentry = rdr1.docentry
                             INNER JOIN oitm ON OITM.itemcode = rdr1.ItemCode
                        WHERE OITM.itemcode BETWEEN @articled AND @articlef
                          AND ordr.DocDate BETWEEN @dated AND @datef
                          AND DocStatus = 'o';
                    END;
                    IF (@afamilled != '')
                   AND @afamillef = ''
                    BEGIN
                        SELECT ordr.docentry,
                               OITM.itemCode,
                               rdr1.dscription,
                               rdr1.quantity,
                               rdr1.price,
                               rdr1.currency,
                               rdr1.slpCode,
                               rdr1.basedocnum,
                               rdr1.shiptocode,
                               rdr1.shiptodesc,
                               rdr1.baseprice,
                               ordr.docnum,
                               ordr.doctype,
                               ordr.docstatus,
                               ordr.docTotal,
                               ordr.docdate,
                               ordr.cardcode,
                               ordr.cardname,
                               ordr.address,
                               ordr.doccur,
                               ordr.paidtodate,
                               ordr.doctime,
                               ordr.docsubtype,
                               ordr.basetype,
                               ordr.baseEntry,
                               OITM.itemclass,
                               OITM.itemtype,
                               OITM.itemname,
                               OITM.itmsgrpcod
                        FROM ordr
                             INNER JOIN rdr1 ON ordr.docentry = rdr1.docentry
                             INNER JOIN oitm ON OITM.itemcode = rdr1.ItemCode
                        WHERE OITM.itemcode BETWEEN @articled AND @articlef
                          AND OITM.itmsgrpcod BETWEEN @afamilled AND ((SELECT MAX(itmsgrpcod)FROM oitm))
                          AND ordr.DocDate BETWEEN @dated AND @datef
                          AND DocStatus = 'o';
                    END;

                    IF (@afamilled = '')
                   AND @afamillef != ''
                    BEGIN
                        SELECT ordr.docentry,
                               OITM.itemCode,
                               rdr1.dscription,
                               rdr1.quantity,
                               rdr1.price,
                               rdr1.currency,
                               rdr1.slpCode,
                               rdr1.basedocnum,
                               rdr1.shiptocode,
                               rdr1.shiptodesc,
                               rdr1.baseprice,
                               ordr.docnum,
                               ordr.doctype,
                               ordr.docstatus,
                               ordr.docTotal,
                               ordr.docdate,
                               ordr.cardcode,
                               ordr.cardname,
                               ordr.address,
                               ordr.doccur,
                               ordr.paidtodate,
                               ordr.doctime,
                               ordr.docsubtype,
                               ordr.basetype,
                               ordr.baseEntry,
                               OITM.itemclass,
                               OITM.itemtype,
                               OITM.itemname,
                               OITM.itmsgrpcod
                        FROM ordr
                             INNER JOIN rdr1 ON ordr.docentry = rdr1.docentry
                             INNER JOIN oitm ON OITM.itemcode = rdr1.ItemCode
                        WHERE OITM.itemcode BETWEEN @articled AND @articlef
                          AND OITM.itmsgrpcod BETWEEN ((SELECT MIN(itmsgrpcod)FROM oitm)) AND @afamillef
                          AND ordr.DocDate BETWEEN @dated AND @datef
                          AND DocStatus = 'o';
                    END;

                    IF (@afamilled != '')
                   AND @afamillef != ''
                    BEGIN
                        SELECT ordr.docentry,
                               OITM.itemCode,
                               rdr1.dscription,
                               rdr1.quantity,
                               rdr1.price,
                               rdr1.currency,
                               rdr1.slpCode,
                               rdr1.basedocnum,
                               rdr1.shiptocode,
                               rdr1.shiptodesc,
                               rdr1.baseprice,
                               ordr.docnum,
                               ordr.doctype,
                               ordr.docstatus,
                               ordr.docTotal,
                               ordr.docdate,
                               ordr.cardcode,
                               ordr.cardname,
                               ordr.address,
                               ordr.doccur,
                               ordr.paidtodate,
                               ordr.doctime,
                               ordr.docsubtype,
                               ordr.basetype,
                               ordr.baseEntry,
                               OITM.itemclass,
                               OITM.itemtype,
                               OITM.itemname,
                               OITM.itmsgrpcod
                        FROM ordr
                             INNER JOIN rdr1 ON ordr.docentry = rdr1.docentry
                             INNER JOIN oitm ON OITM.itemcode = rdr1.ItemCode
                        WHERE OITM.itemcode BETWEEN @articled AND @articlef
                          AND OITM.itmsgrpcod BETWEEN @afamilled AND @afamillef
                          AND ordr.DocDate BETWEEN @dated AND @datef
                          AND DocStatus = 'o';
                    END;
                END;
   

проблема в том, что компилятор, кажется, проверяет внутри каждого оператора if, даже если условия не выполняются, а это занимает около получаса, поэтому я надеюсь найти решение этой проблемы.

1 ответ

Да, как упоминалось в комментариях, компилятор T-SQL SQL Server - это настоящий компилятор, который пытается скомпилировать каждую строку так же, как это делают компиляторы клиентского языка (некоторые компиляторы клиентского языка имеют директивы компилятора, чтобы обойти эту проблему, T-SQL имеет различные методы и функции для достижения той же цели, как описано ниже).

Если вы хотите, чтобы были скомпилированы только некоторые строки, вам нужно будет использовать другой подход. Поскольку ваша хранимая процедура содержит 300000 строк, в любом случае это кажется очень хорошей идеей. Вот некоторые из техник, которые вы могли бы рассмотреть:

  • Разбейте его на главный sProc со всеми решениями (или решениями более высокого уровня) и множество подчиненных sProc, которые имеют фактические запросы SQL (и / или решения более низкого уровня).
  • Динамический SQL для нижних уровней. Динамический SQL не компилируется до тех пор, пока он не будет явно вызван во время выполнения (скрипт SSMS использует подобные методы, поэтому вы можете поискать в нем примеры, еще один пример - универсальные запросы).
  • Используйте генерацию кода на основе таблиц для автоматического создания правильного кода SQL в качестве этапа предварительной компиляции, реализуя один, оба или предыдущие методы.
  • Используйте генерацию кода на основе таблиц, чтобы динамически создавать только тот код SQL, который вам нужен во время выполнения, а затем динамически выполнять его.
  • Любая или все комбинации предшествующих техник.

Имейте в виду, что все, кроме первого пункта, включает динамический SQL и требует достаточных знаний SQL, чтобы понять последствия безопасности (SQL-инъекция и т. Д.) И как с ними бороться . Кроме того, чем дальше вы идете по этому списку, тем более продвинутые технические навыки и знания SQL вам понадобятся для их реализации.

Другие вопросы по тегам