SQL - выберите только строку, которая не дублируется

Мне нужно перенести данные из одной таблицы в другую. Вторая таблица получила ограничение первичного ключа (а первая не имеет ограничений). У них одинаковая структура. Я хочу выбрать все строки из таблицы A и вставить их в таблицу B без повторяющейся строки (если строка равна 0, я хочу взять только первую найденную)

Пример :

MyField1 (PK)   |   MyField2 (PK)   |   MyField3(PK)   |   MyField4   |   MyField5  

----------

1               |   'Test'          |   'A1'           |   'Data1'    |   'Data1'  
2               |   'Test1'         |   'A2'           |   'Data2'    |   'Data2'  
2               |   'Test1'         |   'A2'           |   'Data3'    |   'Data3'  
4               |   'Test2'         |   'A3'           |   'Data4'    |   'Data4'

Как видите, вторая и третья строки получили один и тот же ключ pk, но разные данные в MyField4 и MyField5. Итак, в этом примере я хотел бы иметь первую, вторую и четвертую строку. Не третий, потому что это дублирование второго (даже если MyField4 и MyField5 содержат разные данные).

Как я могу сделать это с помощью одного выбора?

спасибо


person Melursus    schedule 10.02.2009    source источник
comment
MySql, Oracle, MS Sql или что-то еще?   -  person BenMaddox    schedule 10.02.2009


Ответы (4)


Во-первых, вам нужно определить, что делает строку «первой». Я составлю произвольное определение, а вы сможете изменить SQL по своему усмотрению. В этом примере я предполагаю, что «первый» является наименьшим значением для MyField4, а если они равны, то наименьшим значением для MyField5. Это также учитывает возможность того, что все 5 столбцов будут идентичными.

SELECT DISTINCT
     T1.MyField1,
     T1.MyField2,
     T1.MyField3,
     T1.MyField4,
     T1.MyField5
FROM
     MyTable T1
LEFT OUTER JOIN MyTable T2 ON
     T2.MyField1 = T1.MyField1 AND
     T2.MyField2 = T1.MyField2 AND
     T2.MyField3 = T1.MyField3 AND
     (
          T2.MyField4 > T1.MyField4 OR
          (
               T2.MyField4 = T1.MyField4 AND
               T2.MyField5 > T1.MyField5
          )
     )
WHERE
     T2.MyField1 IS NULL

Если вы также хотите учитывать PK, которые не дублируются в исходной таблице, но уже существуют в вашей целевой таблице, вам также необходимо учитывать это.

person Tom H    schedule 10.02.2009

Не уверен, как узнать, какую из строк 2 и 3 вы хотите в новой таблице, но в mysql вы можете просто:

insert ignore into new_table (select * from old_table);

И ПК не позволит вставлять повторяющиеся записи.

person Chris J    schedule 10.02.2009

Какая у вас база данных? В Oracle вы могли бы сказать

SELECT FROM your_table
WHERE rowid in
(SELECT MIN(rowid)
 FROM your_table
 GROUP BY MyField1, MyField2, MyField3);

Обратите внимание, что несколько неясно, какая из строк с одинаковым PK будет считаться «первой». Если вам нужно наложить определенный порядок, вам нужно дополнительно отсортировать по другим столбцам.

person Thilo    schedule 10.02.2009
comment
Будет ли это запускать вложенный оператор select один раз для каждой строки в your_table? Если это так, то вы получите довольно плохую производительность. Надеюсь, вложенный оператор будет кэширован. Не очень знаком с частью планирования запросов. - person Bassam; 10.02.2009
comment
Я использую MS SQL 2005, но я думаю, что этот синтаксис будет работать, я попробую завтра и дам вам знать. Спасибо! - person Melursus; 10.02.2009
comment
Это не будет запускаться для каждой строки, только один раз. - person Thilo; 10.02.2009
comment
в MSSQL нет поля rowid - person Jk.; 10.02.2009

Это зависит от того, что вы ищете.

Существует большая разница между использованием JOIN + WHERE NULL, NOT IN и NOT EXISTS, включая производительность, которая более важна для больших наборов данных.

(См. NOT IN vs. NOT EXISTS vs. LEFT JOIN / IS NULL.)

Три метода, показанные в связанной статье, довольно просты.

person jinglesthula    schedule 11.01.2012