Как указать два выражения в списке выбора, когда подзапрос не введен с EXISTS

У меня есть запрос, который использует подзапрос, и у меня возникла проблема с возвращением ожидаемых результатов. Я получаю сообщение об ошибке: "В списке выбора можно указать только одно выражение, если подзапрос не введен в EXISTS". Как я могу переписать это, чтобы работать?

SELECT
    a.Part,
    b.Location,
    b.LeadTime
FROM
    dbo.Parts a
    LEFT OUTER JOIN dbo.Vendor b ON b.Part = a.Part
WHERE
    b.Location IN ('A','B','C')
AND
    Date IN (SELECT Location, MAX(Date) FROM dbo.Vendor GROUP BY Location)
GROUP BY
    a.Part,
    b.Location,
    b.LeadTime
ORDER BY
    a.Part

3 ответа

Решение

Я думаю, что-то вроде этого может быть то, что вы ищете. Вы не сказали, какая версия SQL Server- это работает в SQL 2005 и выше:

SELECT
    p.Part,
    p.Location, -- from *p*, otherwise if no match we'll get a NULL
    v.LeadTime
FROM
    dbo.Parts p
    OUTER APPLY (
       SELECT TOP (1) * -- * here is okay because we specify columns outside
       FROM dbo.Vendor v
       WHERE p.Location = v.Location -- the correlation part
       ORDER BY v.Date DESC
    ) v
WHERE
    p.Location IN ('A','B','C')
ORDER BY
    p.Part
;

Теперь ваш запрос можно исправить как есть, добавив часть "корреляция", чтобы изменить ваш запрос на correlated subquery как показано в ответе Кори (вы также удалите GROUP BY пункт). Однако этот метод все еще требует дополнительного и ненужного объединения, что снижает производительность, плюс он может извлекать только один столбец за раз. Этот метод позволяет вам извлечь все столбцы из другой таблицы и не имеет дополнительного объединения.

Примечание: это логически дает те же результаты, что и ответ Ламака, однако я предпочитаю его по нескольким причинам:

  1. Когда есть индекс на столбцах корреляции (Locationздесь) это может быть удовлетворено поисками, но Row_Number Решение должно сканировать (я считаю).
  2. Я предпочитаю, чтобы это выражало намерение запроса более прямо и кратко. в Row_Number метод, нужно выйти во внешнее состояние, чтобы увидеть, что мы только хватаем rn = 1 значения, затем вернитесь в CTE, чтобы увидеть, что это такое.
  3. С помощью CROSS APPLY или же OUTER APPLYвсе остальные таблицы, не участвующие в выборе "одна внутренняя строка на внешнюю строку", находятся вне того места, где (для меня) они принадлежат. Мы не скупимся на проблемы вместе. С помощью Row_Number чувствует себя немного как бросать DISTINCT на запрос, чтобы исправить дублирование, а не заниматься основной проблемой. Я думаю, что это в основном та же проблема, что и предыдущий пункт, сформулированный по-другому.
  4. В тот момент, когда у вас есть ДВЕ таблицы, из которых вы хотите извлечь самое последнее значение, Row_Number() раствор взрывается полностью. С этим синтаксисом вы просто легко добавляете другой APPLY пункт, и это совершенно ясно, что вы делаете. Есть способ использовать Row_Number для сценария с несколькими таблицами, перемещая другие таблицы за пределы, но я все еще не предпочитаю этот синтаксис.
  5. Использование этого синтаксиса позволяет выполнять дополнительные объединения в зависимости от того, существует выбранная строка или нет (в случае, если соответствующая строка не была найдена). в Row_Number решение, вы можете только разумно сделать это NOT NULL проверка во внешнем запросе - так что вы вынуждены разделить запрос на несколько отдельных частей (вы не хотите присоединяться к значениям, которые вы отбрасываете!).

PS Я настоятельно рекомендую вам использовать псевдонимы, которые указывают на таблицу, которую они представляют. Пожалуйста, не используйте a а также b, я использовал p за Parts а также v за Vendor- это поможет вам и другим быстрее разобраться в запросе.

Если я вас правильно понял, вы хотите строки с максимальной датой для местоположений A, B и C. Теперь, предполагая SQL Server 2005+, вы можете сделать это:

;WITH CTE AS
(
    SELECT
        a.Part,
        b.Location,
        b.LeadTime,
        RN = ROW_NUMBER() OVER(PARTITION BY a.Part ORDER BY [Date] DESC)
    FROM
        dbo.Parts a
        LEFT OUTER JOIN dbo.Vendor b ON b.Part = a.Part
    WHERE
        b.Location IN ('A','B','C')
)
SELECT  Part, 
        Location,
        LeadTime
FROM CTE
WHERE RN = 1
ORDER BY Part

В вашем подзапросе вы должны соотнести местоположение и деталь с внешним запросом. Пример:

Date = (SELECT MAX(Date) 
        FROM dbo.Vender v 
        WHERE v.Location = b.Location 
          AND v.Part = b.Part
       )

Так что это вернет одну дату для каждого места и части

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