Использовать составные элементы выбора в предложении where

#tsql #syntax

#tsql #синтаксис

Вопрос:

Этот вопрос лучше всего сформулировать на простом примере.

Почему я не могу этого сделать?

 select (lastname   ', '   firstname) as fullname
from people
where fullname = 'Bloggs, Joe'
  

вместо этого я должен сделать это:

 select (lastname   ', '   firstname) as fullname
from people
where (lastname   ', '   firstname) = 'Bloggs, Joe'
  

для меня это плохо пахнет.

Чем сложнее запрос, тем хуже становится эта проблема.

Последующие действия

Вот лучший пример, основанный на реальной проблеме, из которой возник вопрос.

 SELECT ClientID, 
       Name, 
       ContractStartDate, 
       ContractDetails.ContractLength, 
       DATEADD(MONTH, ContractDetails.ContractLength, ContractStartDate) 
           as ContractEndDate
FROM Clients
LEFT OUTER JOIN ContractDetails 
    ON Clients.ClientID = ContractDetails.ClientID
WHERE DATEADD(MONTH, ContractDetails.ContractLength, ContractStartDate) 
      > '2009-06-30'
  

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

 SELECT ClientID, 
       Name, 
       contractStartDate, 
       ContractDetails.ContractLength, 
       contractEndDate
FROM (
      SELECT ClientID, 
             Name, 
             ContractStartDate, 
             DATEADD(MONTH, ContractDetails.ContractLength, contractStartdate) 
               AS contractEndDate
      FROM Clients
      LEFT OUTER JOIN ContractDetails 
        on Clients.ClientID = ContractDetails.ClientID
      ) myview
LEFT OUTER JOIN ContractDetails 
  on myview.ClientID = ContractDetails.ClientID
WHERE myview.ContractEndDate > '2009-06-30'
ORDER BY ClientID
  

Цель этого запроса — найти всех действующих клиентов на определенный момент времени, где не хранятся исторические данные о состоянии (т. Е. вычислить дату окончания контракта на основе известной даты начала и продолжительности контракта).

Кто-нибудь может придумать способ удаления этого повторения?

окончательное выполнение

Робин Дэй помог мне с ключевой вещью, которой мне здесь не хватало, что фактически позволило мне устранить дублирование. Однако у KM есть пункт, где он говорит, что во вложенном представлении должно быть WHERE, а не конечный результат, что потребовало бы дублирования части инструкции (чего я пытаюсь избежать). В данном конкретном случае мне это сойдет с рук, потому что я знаю, что в таблице ContractDetails нет миллионов записей и никогда не будет.

 SELECT ClientID, 
   Name, 
   ContractStartDate, 
   myview.ContractLength, 
   ContractEndDate
FROM (
  SELECT ClientID, 
         Name, 
         ContractStartDate, 
         DATEADD(MONTH, ContractDetails.ContractLength, ContractStartdate) 
           AS ContractEndDate,
         ContractDetails.ContractLength as Length
  FROM Clients
  LEFT OUTER JOIN ContractDetails
    on Clients.ClientID = ContractDetails.ClientID
  ) myview
WHERE myview.ContractEndDate > '2009-06-30'
ORDER BY ClientID
  

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

1. добавьте ContractLength к вложенному представлению, и вам больше не нужно выполнять объединение вне этого представления.

Ответ №1:

Вы можете использовать производную таблицу / вложенное представление…

 select
    fullname
from
(
    select
        (lastname   ', '   firstname) as fullname
    from
        people
) myview
where
    myview.fullname = 'Bloggs, Joe'
  

РЕДАКТИРОВАТЬ: Просто для пояснения, это должно показать концепцию, о которой вы спрашиваете. В этом конкретном примере ваше предложение WHERE должно проверять firstname = ‘Joe’ и lastname = ‘Bloggs’, поскольку KM ответил, а не проверять fullname.

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

1. Мне нравится ваше сообщение об редактировании, в основном повторяющее мой ответ. Тем не менее, мой ответ уже получил два голоса против…

2. @Robin Day : Вы на самом деле не ответили на вопрос, вы просто предоставили обходной путь, чтобы получить ему абстракцию, которую он хотел.

3. @CptSkippy вот почему это не было отмечено как ответ, а у вас есть. Это здорово, потому что это учит меня способу достижения того, что мне нужно, но вы ответили на вопрос, который я задал.

4. @tomfanning, никогда не делай этого таким образом!!!! если у people миллион строк, вы действительно хотите отформатировать все эти имена, чтобы просто выбрать одно из них? это безумно и медленно. ВСЕГДА, ВСЕГДА фильтруйте как можно больше в производных таблицах , точно так же, как я говорю в своем ответе…

5. @km, спасибо за предупреждение, это, конечно, имеет смысл.

Ответ №2:

Список выбора представляет собой преобразование виртуальной таблицы, возвращаемой предложениями from, where и order. Предложения не знают о списке выбора. Кроме того, любые преобразования столбцов, определенные в предложении where, не поддаются саргированию и вынуждают SQL выполнять сканирование таблицы или индекса. Другими словами, это абсолютно снизило бы производительность.

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

1. Краткий ответ на исходный вопрос, указывающий мне, что его просто нужно обойти в другом месте запроса. Большое спасибо.

Ответ №3:

попробуйте:

 select
    (lastname   ', '   firstname) as fullname
    from people
    where lastname = 'Bloggs' AND firstname='Joe'
  

не фильтруйте на основе форматированного вывода «полное имя», фильтруйте на основе столбцов, которые должны быть в индексе.

Редактировать
на основе вашего пересмотренного вопроса:

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

 SELECT ClientID, 
       Name, 
       contractStartDate, 
       ContractDetails.ContractLength, 
       contractEndDate
FROM (
      SELECT ClientID, 
             Name, 
             ContractStartDate, 
             DATEADD(MONTH, ContractDetails.ContractLength, contractStartdate) 
               AS contractEndDate
      FROM Clients
      LEFT OUTER JOIN ContractDetails 
        on Clients.ClientID = ContractDetails.ClientID
      WHERE DATEADD(MONTH, ContractDetails.ContractLength, contractStartdate) > '2009-06-30'
      ) myview
LEFT OUTER JOIN ContractDetails 
  on myview.ClientID = ContractDetails.ClientID
ORDER BY ClientID
  

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

1. -1, я думаю, вы упустили суть — я привел упрощенный пример. Возможно, слишком просто. Бит с псевдонимом инструкции select может содержать большое вычисление, подзапрос, что угодно. Конечно, вы можете фильтровать, используя необработанные поля — смысл был не в этом.

2. @tomfanning, отредактируйте свой вопрос с помощью лучшего примера. Я отвечаю на то, что вы спрашиваете, а не на то, что вы думаете (я не могу читать ваши мысли).

3. @KM, вопрос был «почему я не могу сделать эту конкретную вещь», а не «решить проблему с запросом». Тем не менее, я добавил проблему, связанную с конкретным доменом, и переработку с использованием встроенного представления, основанного на ответе Робина Дэя.

4. @KM спасибо за обновление вашего ответа. Однако это все еще не устранило дублирование.

5. @tomfanning, нет способа удалить дублирование, напишите свой запрос для ускорения и смиритесь с тривиальным дублированием

Ответ №4:

Не слишком ли сложен ваш отредактированный пример? Что не так с:

 SELECT *
FROM (
      SELECT ClientID, 
             Name, 
             ContractStartDate, 
             ContractLength,
             DATEADD(MONTH, ContractDetails.ContractLength, contractStartdate) 
               AS contractEndDate
      FROM Clients
      LEFT OUTER JOIN ContractDetails 
        on Clients.ClientID = ContractDetails.ClientID
      ) myview
WHERE myview.ContractEndDate > '2009-06-30'
ORDER BY ClientID
  

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

1. Если я правильно понимаю его логику, KM уже рекомендовал эту стратегию, потому что DATEADD() вызывается для всего в таблице Clients. Вот что с этим не так.

2. Ваше «окончательное выполнение» точно такое же, как мое предложение выше, за исключением звездочки в моем операторе ВЫБОРА, которая просто устраняет некоторую детализацию.

Ответ №5:

Как заставить этот запрос работать с IN и несколькими столбцами? Это работает в Oracle, но не в T-SQL.

 select (lastname   ', '   firstname) as fullname
from people
where ((lastname, firstname)) IN (('Bloggs', 'Joe'))