SQL LEFT внешнее соединение только с несколькими строками справа? - PullRequest
2 голосов
/ 24 августа 2009

У меня есть две таблицы TABLE_A и TABLE_B с объединенным столбцом в качестве номера сотрудника EMPNO.

Я хочу сделать нормальное левое внешнее соединение. Однако TABLE_B имеет определенные записи, которые были удалены (status='D'), я хочу, чтобы они были включены. Просто чтобы уточнить, TABLE_B может иметь активные записи (статус = ноль / а / что угодно), а также удаленные записи, в этом случае я не хочу, чтобы этот сотрудник в моем результате. Однако, если в TABLE_B есть только удаленные записи сотрудника, я хочу, чтобы сотрудник был включен в результат. Я надеюсь, что разъясняю свое требование. (Я мог бы сделать длинную вещь типа qrslt и получить то, что я хочу, но я думаю, что должен быть более оптимизированный способ сделать это, используя синтаксис соединения). Буду признателен за любые предложения (даже без объединения). Его новичок пытается следующий запрос без желаемого результата:

SELECT TABLE_A.EMPNO
FROM   TABLE_A
LEFT OUTER JOIN TABLE_B ON TABLE_A.EMPNO = TABLE_B.EMPNO AND TABLE_B.STATUS<>'D' 

Очень ценю любую помощь.

Ответы [ 5 ]

2 голосов
/ 24 августа 2009

Просто для пояснения - должны появиться все записи из TABLE_A, если только в таблице B нет строк со статуями , отличных от , отличных от 'D'?

Вам потребуется хотя бы один ненулевой столбец на B (в качестве примера я буду использовать B.ID, и этот подход должен работать):

SELECT TABLE_A.EMPNO
FROM TABLE_A
LEFT OUTER JOIN TABLE_B ON
  (TABLE_A.EMPNO = TABLE_B.EMPNO)
  AND (TABLE_B.STATUS <> 'D' OR TABLE_B.STATUS IS NULL)
WHERE
  TABLE_B.ID IS NULL

То есть измените логику, о которой вы могли подумать - присоединитесь к TABLE_B только там, где есть строки, которые исключают записи TABLE_A, а затем используйте IS NULL в конце, чтобы исключить их. Это означает, что включаются только те, которые не соответствуют (те, у которых нет строки в TABLE_B или только строки 'D').

Альтернативой может быть

SELECT TABLE_A.EMPNO
FROM TABLE_A
WHERE NOT EXISTS (
  SELECT * FROM TABLE_B 
  WHERE TABLE_B.EMPNO = TABLE_A.EMPNO
  AND (TABLE_B.STATUS <> 'D' OR TABLE_B.STATUS IS NULL)
)
0 голосов
/ 10 июня 2011

Вот как я понимаю вопрос. Вам нужно включить только тех сотрудников, для которых выполняется одно из следующих условий:

  • сотрудник удалил только (мягкие) строки в TABLE_B;

  • у сотрудника есть только не удаленные строки в TABLE_B;

  • у сотрудника нет строк в TABLE_B.

Другими словами, если сотрудник имеет как удаленные, так и не удаленные строки в TABLE_B, опустите этого сотрудника, в противном случае включите их.

Вот как я думаю, что это можно решить:

SELECT DISTINCT a.EMPNO
FROM TABLE_A a
  LEFT JOIN TABLE_B b1 ON a.EMPNO = b1.EMPNO
  LEFT JOIN TABLE_B b2 ON b1.EMPNO = b2.EMPNO
    AND (b1.STATUS = 'D' AND (b2.STATUS <> 'D' OR b2 IS NULL) OR
         b2.STATUS = 'D' AND (b1.STATUS <> 'D' OR b1 IS NULL))
WHERE b2.EMPNO /* or whatever non-nullable column there is */ IS NULL

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

SELECT a.EMPNO
FROM TABLE_A a
  LEFT JOIN TABLE_B b ON a.EMPNO = b1.EMPNO
GROUP BY a.EMPNO
HAVING 0 IN (COUNT(CASE b.STATUS WHEN 'D' THEN 1 ELSE NULL END),
             COUNT(CASE b.STATUS WHEN 'D' THEN NULL ELSE 1 END))
0 голосов
/ 10 июня 2011
SELECT A.*, B.*
FROM
   Table_A A
   INNER JOIN Table_B B
      ON A.EmpNo = B.EmpNo
WHERE
   NOT EXISTS (
      SELECT *
      FROM Table_B X
      WHERE
          A.EmpNo = X.EmpNo
          AND X.Status <> 'D'
    )

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

0 голосов
/ 24 августа 2009

В следующем запросе вы получите записи о сотрудниках, которые не были удалены, или только сотрудники удалили записи.

select
    a.*
from
    table_a a
    left join table_b b on
        a.empno = b.empno
where
    b.status <> 'D'
    or (b.status = 'D' and 
        (select count(distinct status) from table_b where empno = a.empno) = 1)

Это в ANSI SQL, но если бы я знал вашу RDBMS, я мог бы дать более конкретное решение, которое может быть немного более элегантным.

0 голосов
/ 24 августа 2009

ах, это очевидно работает> <</p>

SELECT TABLE_A.EMPNO
FROM   TABLE_A
LEFT OUTER JOIN TABLE_B ON TABLE_A.EMPNO = TABLE_B.EMPNO 
where TABLE_B.STATUS<>'D'

Если у вас есть дополнительная информация, пожалуйста, не стесняйтесь.

UPDATE: Видел этот вопрос через некоторое время и подумал, что я добавлю больше полезной информации: Эта ссылка содержит хорошую информацию о синтаксисе ANSI - http://www.oracle -base.com / Articles / 9i / ANSIISOSQLSupport.php

В частности, эта часть со связанной страницы является информативной:

Дополнительные условия фильтра могут быть добавлены к соединению с использованием AND для формирования сложного объединения. Они часто необходимы, когда условия фильтра необходимы для ограничения внешнего объединения. Если эти условия фильтра помещены в предложение WHERE, а внешнее соединение возвращает значение NULL для столбца фильтра, строка будет отброшена. если условие фильтра закодировано как часть объединения, ситуации можно избежать.

...