Есть ли лучший способ организовать и сделать этот запрос SQL более читабельным? - PullRequest
4 голосов
/ 07 июля 2010

Я унаследовал часть SQL, над которой я работаю, для системы отчетности. Система сосредоточена вокруг заказов на поставку, а также когда они создаются, передаются, подтверждаются и подтверждаются. Эти государства являются прогрессивным государством. Часть SQL, которую я рассматриваю, показана ниже и взята из предложения WHERE:

OR (CreateJob.endtime is NULL and DATEDIFF(hour, buy_date, getdate()) > Vendor.expected_create_hours)
OR (TransmitJob.endtime is NULL and DATEDIFF(hour, CreateJob.endtime, getdate()) > Vendor.expected_transmit_hours)
OR (AcknowledgeJob.endtime is NULL and DATEDIFF(hour, TransmitJob.endtime, getdate()) > Vendor.expected_acknowledge_hours)
OR (ConfirmJob.endtime is NULL and DATEDIFF(hour, AcknowledgeJob.endtime, getdate()) > Vendor.expected_confirm_hours)

Что я обнаружил, так это то, что CreateJob может не иметь времени окончания, потому что задание не выполнено. Однако задание может выполнять более одного Заказа на поставку, поэтому бывают случаи, когда конкретный заказ был фактически создан, но задание не получает время окончания, потому что позднее произошел сбой одного из братьев и сестер. Это создает сценарий, в котором состояния все еще выполняются для PO, но он все еще отображается в этом отчете о проблеме, потому что CreateJob.endtime имеет значение NULL.

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

Похоже, я мог бы исправить отчет, просто добавив прогрессивные проверки в операторы AND. Так что первая проверка для CreateJob тоже меняется:

(     (CreateJob.endtime is NULL and DATEDIFF(hour, buy_date, getdate()) > Vendor.expected_create_hours) 
  AND (TransmitJob.endtime is NULL and DATEDIFF(hour, CreateJob.endtime, getdate()) > Vendor.expected_transmit_hours) 
  AND (AcknowledgeJob.endtime is NULL and DATEDIFF(hour, TransmitJob.endtime, getdate()) > Vendor.expected_acknowledge_hours) 
  AND (ConfirmJob.endtime is NULL and DATEDIFF(hour, AcknowledgeJob.endtime, getdate()) > Vendor.expected_confirm_hours))

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

CreateFailed = (CreateJob.endtime is NULL and DATEDIFF(hour, buy_date, getdate()) > Vendor.expected_create_hours)
TransmitFailed = (TransmitJob.endtime is NULL and DATEDIFF(hour, CreateJob.endtime, getdate()) > Vendor.expected_transmit_hours)
AcknowledgeFailed = (AcknowledgeJob.endtime is NULL and DATEDIFF(hour, TransmitJob.endtime, getdate()) > Vendor.expected_acknowledge_hours)
ConfirmFailed = (ConfirmJob.endtime is NULL and DATEDIFF(hour, AcknowledgeJob.endtime, getdate()) > Vendor.expected_confirm_hours)


OR (CreateFailed AND TransmitFailed AND AcknowledgeFailed AND ConfirmFailed)
OR (TransmitFailed AND AcknowledgeFailed AND ConfirmFailed)
OR (AcknowledgeFailed AND ConfirmFailed)
OR (ConfirmFailed)

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

Ответы [ 2 ]

2 голосов
/ 07 июля 2010

Побочный момент - является ли фрагмент кода, который вы предоставили, выдержкой из рабочего кода? Если это так, вы можете заменить весь блок только условием ConfirmFailed, поскольку оно является общим для всех показанных вами условий WHERE и может удовлетворить его самостоятельно.

Хотя это не дает прямого ответа на вопрос, который вы задали, подход к упрощению вашего кода будет заключаться в том, чтобы рассчитать значения для каждого индикатора Failed один раз на этапе предварительной обработки - в зависимости от вашей среды это может быть CTE , временная таблица или представление. Таким образом, вся логика находится в одном месте - это особенно полезно, если значение требуется более одного раза в запросе - например, в предложениях SELECT и WHERE.

1 голос
/ 07 июля 2010

[есть] есть общий термин или имя за что я пытаюсь сделать ...?

Абстракция

Вы можете скрыть каждую свою CreateFailed = ..., TransmitFailed = ... и т. Д. Логику в ее собственной VIEW или CTE, тогда ваш основной запрос может просто посмотреть, существует ли задание в одной из этих таблиц, например, (псевдо SQL):

SELECT Jobs.job_ID, ...
  FROM Jobs
 WHERE EXISTS (
               SELECT * 
                 FROM CreatedFailedJobs AS T1
                WHERE Jobs.job_ID = T1.job_ID
               )
       OR EXISTS (
                  SELECT * 
                    FROM TransmitFailedJobs AS T1
                   WHERE Jobs.job_ID = T1.job_ID
                  )
       OR EXISTS (
                  SELECT * 
                    FROM AcknowledgeFailedJobs AS T1
                   WHERE Jobs.job_ID = T1.job_ID
                  )
       OR EXISTS (
                  SELECT * 
                    FROM ConfirmFailedJobs AS T1
                   WHERE Jobs.job_ID = T1.job_ID
                  );

Вот расширенная идея CTE (псевдо SQL):

WITH CreateFailedJobs (job_ID, ...)
AS
(
 SELECT Jobs.job_ID, ...
   FROM ...
  WHERE CreateJob.endtime is NULL 
        AND DATEDIFF(hour, .SomeTable.buy_date, getdate()) 
               > Vendor.expected_create_hours
), 
TransmitFailedJobs (job_ID, ...)
AS
(
 SELECT Jobs.job_ID, ...
   FROM ...
  WHERE TransmitJob.endtime is NULL 
        AND DATEDIFF(hour, CreateJob.endtime, getdate()) 
               > Vendor.expected_transmit_hours
), 
AcknowledgeFailedJobs (job_ID, ...)
AS
(
 SELECT Jobs.job_ID, ...
   FROM ...
  WHERE AcknowledgeJob.endtime is NULL 
        AND DATEDIFF(hour, TransmitJob.endtime, getdate()) 
               > Vendor.expected_acknowledge_hours
), 
ConfirmFailedJobs (job_ID, ...)
AS
(
 SELECT Jobs.job_ID, ...
   FROM ...
  WHERE ConfirmJob.endtime is NULL 
        AND DATEDIFF(hour, AcknowledgeJob.endtime, getdate()) 
               > Vendor.expected_confirm_hours)
)
SELECT Jobs.job_ID, ...
  FROM Jobs
 WHERE EXISTS (
               SELECT * 
                 FROM CreatedFailedJobs AS T1
                WHERE Jobs.job_ID = T1.job_ID
               )
       OR EXISTS (
                  SELECT * 
                    FROM TransmitFailedJobs AS T1
                   WHERE Jobs.job_ID = T1.job_ID
                  )
       OR EXISTS (
                  SELECT * 
                    FROM AcknowledgeFailedJobs AS T1
                   WHERE Jobs.job_ID = T1.job_ID
                  )
       OR EXISTS (
                  SELECT * 
                    FROM ConfirmFailedJobs AS T1
                   WHERE Jobs.job_ID = T1.job_ID
                  );

Я не уверен, что это было бы полезно для производительности, но целью является читабельность, и в любом случае этот отчет можно запустить в автономном режиме.

...