Выберите первую запись в отношении «один ко многим», используя левое соединение - PullRequest
41 голосов
/ 17 ноября 2011

Я пытаюсь объединить две таблицы, используя левое соединение.И набор результатов должен включать только первую запись из «правой» объединенной таблицы.

Допустим, у меня есть две таблицы A и B, как показано ниже;

Таблица «A»

code | emp_no

101  | 12222
102  | 23333
103  | 34444
104  | 45555
105  | 56666

Таблица "B"

code | city       | county
101  | Glen Oaks  | Queens
101  | Astoria    | Queens
101  | Flushing   | Queens
102  | Ridgewood  | Brooklyn
103  | Bayside    | New York

Ожидаемый результат:

code | emp_no | city      | county
101  | 12222  | Glen Oaks | Queens
102  | 23333  | Ridgewood | Brooklyn
103  | 34444  | Bayside   | New York
104  | 45555  | NULL      | NULL
105  | 56666  | NULL      | NULL

Есливы заметили, что мой результат имеет только одну сопоставленную запись из таблицы "B" (не имеет значения, с какой записью сопоставляются) после левого соединения (и это сопоставление один ко многим)

Мне нужно выбрать первуюсопоставить запись из таблицы B и игнорировать все остальные строки.

Пожалуйста, помогите!

Спасибо

Ответы [ 7 ]

33 голосов
/ 17 ноября 2011

Немного поиграв, это оказывается сложнее, чем я ожидал!Предполагая, что table_b имеет некоторый уникальный столбец (скажем, первичный ключ с одним полем), похоже, что вы можете сделать это:

SELECT table_a.code,
       table_a.emp_no,
       table_b.city,
       table_b.county
  FROM table_a
  LEFT
  JOIN table_b
    ON table_b.code = table_a.code
   AND table_b.field_that_is_unique =
        ( SELECT TOP 1
                 field_that_is_unique
            FROM table_b
           WHERE table_b.code = table_a.code
       )
;
7 голосов
/ 24 августа 2016

Другой вариант: OUTER APPLY

Если поддерживается базой данных, OUTER APPLY - эффективный и краткий вариант.

SELECT *
FROM 
    Table_A a
OUTER APPLY
    (SELECT TOP 1 * 
    FROM Table_B b_1
    WHERE b_1.code = a.code
    ) b
;

Это приводит к левому соединению с неопределенный первая найденная запись.Мои тесты показывают, что это быстрее, чем любое другое опубликованное решение (на MS SQL Server 2012).

6 голосов
/ 19 января 2016

Ответ, получивший наибольшее количество голосов, не кажется мне правильным и выглядит слишком сложным. Просто сгруппируйте по полю кода в таблице B в своем подзапросе и выберите максимальный Id для группировки.

SELECT 
    table_a.code,
    table_a.emp_no,
    table_b.city,
    table_b.county
FROM 
    table_a
    LEFT JOIN 
        table_b
        ON table_b.code = table_a.code
        AND table_b.field_that_is_unique IN
            (SELECT MAX(field_that_is_unique)
             FROM table_b
             GROUP BY table_b.code)
4 голосов
/ 17 ноября 2011

Если вы используете SQL Server 2005 или более позднюю версию, вы можете использовать рейтинг для достижения того, что вы хотите. В частности, ROW_NUMBER(), кажется, удовлетворит ваши потребности:

WITH B_ranked AS (
  SELECT
    *,
    rnk = ROW_NUMBER() OVER (PARTITION BY code ORDER BY city)
  FROM B
)
SELECT
  A.code,
  A.emp_no,
  B.city,
  B.county
FROM A
  LEFT JOIN B_ranked AS B ON A.code = B.code AND b.rnk = 1

OR

WITH B_unique_code AS (
  select * from(
     SELECT
      *,
      rnk = ROW_NUMBER() OVER (PARTITION BY code ORDER BY city)
      FROM B
     ) AS s
  where rnk = 1
)
SELECT
  A.code,
  A.emp_no,
  B.city,
  B.county
FROM A
  LEFT JOIN B_unique_code AS B ON A.code = B.code
2 голосов
/ 12 августа 2015

Я изменил ответ от ruakh , и это, похоже, прекрасно работает с mysql.

SELECT 
   table_a.code,
   table_a.emp_no,
   table_b.city,
   table_b.county
FROM table_a a
LEFT JOIN table_b b
ON b.code = a.code 
AND b.id = (  SELECT id FROM table_b 
              WHERE table_b.code = table_a.code 
              LIMIT 1
           )
;
1 голос
/ 14 декабря 2013

В Oracle вы можете сделать:

WITH first_b AS (SELECT code, min(rowid) AS rid FROM b GROUP BY code)) 
SELECT a.code, a.emp_no, b.city, b.county
FROM a 
INNER JOIN first_b 
 ON first_b.code = a.code
INNER JOIN b
 ON b.rowid = first_b.rid
0 голосов
/ 17 ноября 2011

вот как:

 Select * From TableA a
     Left Join TableB b
         On b.Code = a.Code 
             And [Here put criteria predicate that 'defines' what the first record is]

Эй, если город и округ уникальны, то используйте их

   Select * From TableA a
     Left Join TableB b
         On b.Code = a.Code 
             And b.City + b.county =
                  (Select Min(city + county)
                   From TableB 
                   Where Code = b.Code)

Но дело в том, что вы должны поместить туда некоторое выражение, чтобы сообщить обработчику запросов, что означает , чтобы быть first .

...