2016-12-01 4 views
0

Я работаю со следующим запросом:не позволяет несколько СЛУЧАЯ подзапроса результатов при использовании IN

SELECT * 
FROM TableA a 
WHERE a.FieldA IN (

    CASE 

    --select subquery returns a single value 
    WHEN a.FieldB = 'Value1' 
     THEN (select b.ID from TableB b where b.FK_Field = '123') 

    --select subquery returns multiple values 
    WHEN a.FieldB = 'Value2' 
     THEN (select c.ID from TableC c where c.FK_Field = '123') 

    END 
) 

первом случае выберите возвращают выписки только один b.ID. Если у меня просто есть это утверждение, мой код работает.

Второй оператор case, однако, возвращает несколько c.ID s. Когда я добавить этот чек, я получаю следующее сообщение об ошибке:

Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.

Если я бы WHERE a.FieldA =, то я понимаю, что подзапрос может возвращать только 1 значение. У меня, однако, есть WHERE a.FieldA IN, так почему он жалуется, если возвращено несколько значений?

Как я могу реализовать этот вид чека?

+2

'CASE' в T-SQL является ** выражением ** (например,' a + b'), которое возвращает ** одиночное, атомное значение ** - вы ** не можете использовать его для выборочного запуска SQL-фрагментов которые возвращают целые результирующие наборы ... –

+0

@marc_s Я предполагаю, что эта ошибка попадет в категорию *, когда подзапрос используется как выражение *. Есть ли у вас какие-либо предложения о том, как я могу выполнять этот вид проверки, не используя 'CASE', или каким-то образом позволяя несколько результатов? –

+0

Возможно, [это] (http://stackoverflow.com/a/10260297/92546) ответ предложит некоторые идеи. – HABO

ответ

0

Как @marc_s объяснил в комментарии:

CASE in T-SQL is an expression (like a+b) which returns a single, atomic value - you cannot use it to selectively run SQL snippets that return entire result sets

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

SELECT * 
FROM TableA a 
WHERE 
    (a.FieldB = 'Value1' 
     AND a.FieldA IN (select b.ID from TableB b where b.FK_Field = '123')) 

    OR (a.FieldB = 'Value2' 
     AND a.FieldA IN (select c.ID from TableC c where c.FK_Field = '123')) 

Этот код немного грязнее, чем CASE заявление, но это работает.

+0

И может также извлечь пользу из индекса. – Deadsheep39

0

много способов сделать это, вот один из способов, используя объединение всех и коррелируют СУЩЕСТВУЕТ Заявление о

;WITH cte AS (
    SELECT 'Value1' as FieldB, b.Id 
    FROM 
     TableB 
    WHERE 
     b.FK_FieldId = '123' 

    UNION ALL 

    SELECT 'Value1' as FieldB, c.Id 
    FROM 
     TableB 
    WHERE 
     c.FK_FieldId = '123' 
) 



SELECT * 
FROM 
    TableA a 
WHERE 
    EXISTS (SELECT 1 
      FROM 
      cte c 
      WHERE 
      a.FieldB = c.FieldB 
      AND a.FieldA = c.Id) 

Проблема с тем, как вы написали это то, что вы получаете, не скалярное значение (что означает более 1 строки), где sql ожидает скалярное значение. В случае выражения могут использоваться только скалярные значения в части THEN, а также некоторые правила в WHEN. Чтобы решить, вам нужно разделить выражение вашего случая на несколько операторов и/или использовать какую-либо другую технику, такую ​​как приведенная выше.

Или вы могли бы написать выражение случай так:

SELECT * 
FROM 
    TableA a 
WHERE 
    (CASE 
     WHEN a.FieldB = 'Value1' AND a.FieldA IN (select b.ID from TableB b where b.FK_Field = '123') THEN 1 
     WHEN a.FieldB = 'Value2' AND a.FieldA IN (select c.ID from TableC c where c.FK_Field = '123') THEN 1 
     ELSE 0 
    END) = 1 
+0

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

+1

Это будет зависеть от вашего набора данных, но фактически его переключение на использование существует индивидуально, возможно, еще быстрее. http://stackoverflow.com/questions/173041/not-in-vs-not-exists Ответ, который вы опубликовали, также работает, что может быть быстрее, но вам нужно будет протестировать. При использовании IN это очень важно, чтобы список не содержал NULL, который, вероятно, не будет проблемой при использовании идентификаторов, но если по какой-то причине он содержит NULL, вы получите все совпадения, потому что SQL не может сравнивать нуль – Matt

+0

Спасибо за предложения. Я проведу некоторое тестирование, чтобы узнать, какой метод лучше всего подходит для моего набора данных. –

0

Не используйте case в предикатах, если это не нужно - используя случай сделать свой аргумент non-SARG (вы QRY не будет использовать индекс).

SELECT * 
FROM TableA a 
WHERE EXISTS(
     SELECT NULL 
     FROM TableB b 
     WHERE a.FieldB = 'Value1' 
      AND b.FK_Field = '123' 
      AND a.FieldA = b.ID)) 
    OR EXISTS(
     SELECT NULL 
     FROM TableC c 
     WHERE a.FieldB = 'Value2' 
      AND c.FK_Field = '123' 
      AND a.FieldB = c.ID)) 

Использовать индексы. И попробуйте сделать ваш qry доступным для чтения.

Или, если вы хотите использовать Полусоединение:

SELECT * 
FROM TableA a 
WHERE a.FieldA IN (
     SELECT b.ID 
     FROM TableB b 
     WHERE a.FieldB = 'Value1' 
      AND b.FK_Field = '123')) 
    OR a.FieldB IN (
     SELECT c.ID 
     FROM TableC c 
     WHERE a.FieldB = 'Value2' 
      AND c.FK_Field = '123')) 

Оба этих растворов с SARG.

+1

Можете ли вы объяснить, что такое SARG? И я думал, что операторы CASE повышают эффективность, так как оператор заканчивается, как только будет найдено совпадение? –

+0

SARG означает поиск аргумента. Это важно для создания qries. В скором времени это означает, что ваш qry сможет использовать индексный план или лучший план выполнения. – Deadsheep39