Выберите первую строку в соединении двух таблиц в одном операторе - PullRequest
9 голосов
/ 22 апреля 2009

Мне нужно выбрать только первую строку в запросе, который объединяет таблицы A и B. В таблице B существует несколько записей с одинаковым именем. Ни в одной из двух таблиц нет идентификаторов. Я также не могу изменить схему, потому что мне не принадлежит БД.

TABLE A
NAME

TABLE B
NAME
DATA1
DATA2

Select Distinct A.NAME,B.DATA1,B.DATA2 
From A 
Inner Join B on A.NAME = B.NAME

Это дает мне

NAME       DATA1    DATA2
sameName   1        2
sameName   1        3
otherName  5        7
otherName  8        9

но мне нужно получить только одну строку для имени

NAME       DATA1    DATA2
sameName   1        2
otherName  5        7

Мне удалось сделать это, добавив результат во временную таблицу со столбцом идентификаторов, а затем выбрав минимальный идентификатор для имени.

Проблема в том, что мне нужно сделать это в одном утверждении.

Ответы [ 8 ]

10 голосов
/ 10 июня 2009

Это будет работать:

with temp as (
    select A.NAME, B.DATA1, B.DATA2, 
        row_number() over (partition by A.NAME order by A.NAME) as rownum
    from TABLEA A inner join TABLEB B
    on A.NAME = B.NAME
)
select NAME, DATA1, DATA2 from temp where rownum = 1

Если вы хотите выбрать наименьшее значение данных1 и внутри него данных2, используйте этот вариант:

with temp as (
    select A.NAME, B.DATA1, B.DATA2, 
        row_number() over (partition by A.NAME order by B.DATA1, B.DATA2) as rownum
    from TABLEA A inner join TABLEB B
    on A.NAME = B.NAME
)
select NAME, DATA1, DATA2 from temp where rownum = 1

Оба запроса дадут по одной строке на имя.

7 голосов
/ 22 апреля 2009

Использование GROUP BY может помочь вам частично, но будьте осторожны. Если вы делаете что-то вроде этого:

Select A.NAME, min(B.DATA1), min(B.DATA2) 
From A Inner Join B on A.NAME = B.NAME 
Group by A.NAME;

Вы получите результат, который ищете:

  NAME      DATA1   DATA2
  sameName   1        2    
  otherName  5        7

Но только из-за данных, которые вы тестируете. Если вы измените данные, чтобы вместо:

otherName  8        9

у вас было:

otherName  8        4

Возвращается:

  NAME      DATA1   DATA2
  sameName   1        2    
  otherName  5        4

Обратите внимание, что otherName не возвращает DATA1 и DATA2 из одной и той же записи!

Обновление: самостоятельное объединение со сравнением одного из значений данных может помочь вам, например:

SELECT a.*, b.* FROM a,b 
   LEFT JOIN b b2 ON b.name = b2.name AND b.data2 < b2.data2 
   WHERE a.name = b.name AND b2.data2 IS NOT NULL;

Однако это будет работать только в том случае, если значения в DATA2 уникальны для ИМЯ.

0 голосов
/ 06 мая 2017

Вы можете использовать номер строки, чтобы получить одну строку для каждого имени, попробуйте что-то вроде ниже

Select name,data1,data2 from 
(Select A.NAME,B.DATA1,B.DATA2,row_number() over(partitioj by a.name order by a.name) rn
From A 
Inner Join B on A.NAME = B.NAME) where rn=1
0 голосов
/ 22 февраля 2017

Тег этого вопроса указывает, что это будет решение для DB2, но это очень похоже на сервер MS-SQL, если так, попробуйте следующие решения:

Используя CROSS, можно будет отобразить то, что существует только в обеих таблицах

select A.*, B.DATA1, B.DATA2
from A
cross apply (select top 1 * from B where B.name = A.name) B

Но можно изменить на OUTER, чтобы отобразить то, что существует в A без обязательства существовать в B

select A.*, B.DATA1, B.DATA2
from A
OUTER apply (select top 1 * from B where B.name = A.name) B

В структуру оператора apply также можно включить оператор ORDER, поскольку в таблице B

отсутствует указание порядка выходов.
0 голосов
/ 03 апреля 2012
SELECT  A.NAME, bb.DATA1, bb.DATA2 
From A Inner Join B on A.NAME = B.NAME
WHERE B.DATA1 = (SELECT MIN(DATA1) FROM B WHERE NAME = A.NAME)

Это даст желаемый результат при условии, что значения B.DATA1 равны уникально в наборе, относящемся к таблице A.

Если они не уникальны, я знаю только другой способ - использовать CROSS APPLY в MSSQL 2005 и более поздних версиях.

0 голосов
/ 22 апреля 2009

Попробуйте дедуплицировать B, как это

SELECT  A.NAME, bb.DATA1, bb.DATA2 
FROM    A 
JOIN    B bb
ON      A.NAME = B.NAME
WHERE   NOT EXISTS (SELECT  *
                    FROM    B
                    WHERE   NAME = bb.NAME
                            AND (DATA1 > bb.DATA1
                                OR DATA1 = bb.DATA1 AND DATA2 > bb.DATA2))

Добавление дополнительных предложений OR, если существует больше столбцов DATAx.

Если A также содержит дубликаты, просто используйте DISTINCT, как в OP.

0 голосов
/ 22 апреля 2009

Если вы можете добавить к временной таблице и затем запросить ее, вы можете сделать это за один раз.

WITH T AS (temp table select), RN AS (select min row-numbers from T) SELECT T.NAME, T.DATA1, T.DATA2 FROM T INNER JOIN RN on T.row_number = RN.row_number

Есть много других способов написать это, но вот как я делал подобные вещи.

0 голосов
/ 22 апреля 2009

Не уверен, решит ли это вашу проблему или нет, но вы можете попробовать использовать предложение GROUP BY и группировать по одному из столбцов имени.

DB2 Group по учебному пособию

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...