Левое соединение с И все еще фильтрует строки

#sql #sql-server #join

#sql #sql-сервер #Присоединиться

Вопрос:

У меня есть следующий запрос:

 SELECT c.cardNo, acc.accountId
FROM card c
LEFT JOIN accounts acc ON c.accId = acc.id
  

Приведенный выше запрос возвращает 500 строк. В приведенном выше запросе я извлекаю все карточки. Я притворяюсь, что вижу, что карточки, у которых нет учетной записи (просто в качестве примера), вернут NULL вместо AccountId.

Затем я хотел получить все карточки и только идентификатор учетной записи стран, зарегистрированных в Европе. Итак, я все еще хотел 500 карточек, поэтому я попробовал следующее:

 SELECT c.cardNo, acc.accountId
FROM card c
LEFT JOIN accounts acc ON c.accId = acc.id AND acc.registrationContinent = 'EU'
  

Тем не менее, к моему удивлению, я получаю 300 строк (а не 500), приведенное выше также эквивалентно, как если бы у меня было предложение WHERE.

Кто-нибудь может любезно объяснить, почему левое соединение возвращает не все записи из таблицы card?

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

1. Все ли 500 карточек имеют континент регистрации как «EU»

2.Если вы просто выполните SELECT * FROM card , сколько строк вы получите? Несмотря на название, возможно ли, чтобы в было несколько accounts строк, которые имеют одинаковое id значение?

3. Можете ли вы jst увидеть отдельный registration_continent из приведенного выше запроса..

4. И нет, это не похоже на предложение where . Если вы добавите acc.registrationContinent = 'EU' в предложение where, вы не получите карточки без учетной записи. В то время как ваш второй запрос также выдает эти результаты.

Ответ №1:

Вы указали условие в соединении, теперь это будет частью того, как таблицы сопоставляются друг с другом. Чтобы получить желаемые результаты, вы можете использовать подзапрос для фильтрации результатов, которые вы присоединяете к своим картам. Вот так:

 SELECT c.cardNo,
    acc.accountId
FROM card c
LEFT JOIN (
    SELECT accountId
    FROM accounts
    WHERE registrationContinent = 'EU'
    ) acc
    ON c.accId = acc.id
  

Ответ №2:

потому что вы фильтруете учетные записи, которые не находятся в ‘EU’.

Если вам нужно нулевое значение в acc.accountid, когда account.registrationContinent отсутствует EU , вы можете сделать

 SELECT c.cardNo, 
case when acc.registrationContinent = 'EU' then acc.accountId else NULL end
FROM card c
LEFT JOIN accounts acc ON c.accId = acc.id
  

Но я думаю, что у вас много учетных записей по карточкам, некоторые с EU, некоторые без EU.

Итак, с вашим вторым запросом у вас все еще есть все ваши карты (могу поспорить, что у вас менее 500 разных карт и, вероятно, менее 300 разных карт).

Первый запрос будет возвращать несколько раз одну и ту же карточку, если у нее более одной учетной записи (каждый раз с другой учетной записью).

Просто проверьте

 select count(*) from card
  

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

Ответ №3:

Когда у вас есть условие в инструкции join, фильтруются первые данные, затем происходит объединение. Когда вы указываете условие в WHERE, сначала происходит соединение, затем данные фильтруются.

Приведенный ниже пример может вам помочь..

 DECLARE @A TABLE (ID INT, NAME VARCHAR(5), DEPID INT)
DECLARE @B TABLE (DEPID INT, DEPNAME VARCHAR(5),COUNTRY VARCHAR(5))
INSERT INTO @A VALUES (1,'JIT',101),(2,'PAT',101),(3,'JOM',101),(4,'MARK',102),(5,'FAL',103)
INSERT INTO @B VALUES (101,'HR','IND'),(102,'ACC','IND'),(103,'OPER','US')

SELECT      * FROM @A
SELECT      * FROM @B
  

Результат

введите описание изображения здесь

 SELECT      *
FROM        @A A 
LEFT JOIN   @B B ON A.DEPID = B.DEPID

SELECT      *
FROM        @A A 
LEFT JOIN   @B B ON A.DEPID = B.DEPID AND B.COUNTRY = 'US'
  

Результат

введите описание изображения здесь