2014-12-18 5 views
2

У меня есть две плохо разработанные таблицы Form и FormDetails, которые я пытаюсь очистить.Как отфильтровать дубликаты?

В таблице Форма содержит информацию о состоянии формы в моем приложении:

+-----------------------------------------+ 
| form_id | status_id | form_created_by | 
+-----------------------------------------+ 
| 1  | 1   | abc    | 
+-----------------------------------------+ 
| 2  | 3   | def    | 
+-----------------------------------------+ 

form_id, в виде таблицы, является первичным ключом.

В таблице FormDetails имеет дополнительную информацию о форме:

+-----------------------------------------+ 
| form_id | status_id | process_id  | 
+-----------------------------------------+ 
| 1  | 1   | 1    | 
+-----------------------------------------+ 
| 2  | 2   | 1    | 
+-----------------------------------------+ 
| 2  | 3   | 1    | 
+-----------------------------------------+ 
| 2  | 3   | 1    | 
+-----------------------------------------+ 

form_id, в виде таблицы, является НЕ первичного или внешнего ключа. Нет ограничений. Эта таблица плохо разработана, и со временем дублирующиеся данные были добавлены в эту таблицу.

Я хотел бы очистить эту таблицу, скопировав уникальные данные в новую таблицу FormDetails и сделав form_id внешним ключом в таблице форм.

Для того, чтобы достичь этого я попробовал следующий запрос:

select * 
from FormDetails fd 
right join Form f on f.form_id = fd.form_id and f.status_id = fd.status_id 

К сожалению, я все еще получаю строки с повторяющимся form_id так form_id = 2 имеет два ряда с status_id = 3.

+-----------------------------------------+ 
| form_id | status_id | process_id  | 
+-----------------------------------------+ 
| 1  | 1   | 1    | 
+-----------------------------------------+ 
| 2  | 3   | 1    | 
+-----------------------------------------+ 
| 2  | 3   | 1    | 
+-----------------------------------------+ 

Я пытаюсь написать запрос, который говорит: выбрать все строки в FormDetails, которые соответствуют текущей форме. Если есть дубликаты, просто выберите один из них.

Любая идея, как я мог написать такой запрос?

То, что я хотел бы видеть это:

+-----------------------------------------+ 
| form_id | status_id | process_id  | 
+-----------------------------------------+ 
| 1  | 1   | 1    | 
+-----------------------------------------+ 
| 2  | 3   | 1    | 
+-----------------------------------------+ 
+0

Какую базу вы используете? –

+0

@GordonLinoff SQL Server 2005. Я теперь добавил sql-сервер в качестве тега ... – mezoid

+0

Я иду по памяти, но я думаю, что SELECT DISTINCT для MSSQL должен сделать трюк. – Jonathan

ответ

0

С помощью друзей, мне удалось найти ответ на эту проблему. Самый близкий ответ на мой вопрос - @Gordon Linoff, но это все еще не совсем то, что я искал.

Для того, чтобы определить, сколько строк в таблице формы не было соответствующей записи в таблице FormDetails мы написали следующий запрос:

SELECT * 
FROM Form 
LEFT OUTER JOIN FormDetails ON Form.form_id = FormDetails.form_id 
      AND Form.status_id = FormDetails.status_id 
WHERE 
     FormDetails.form_id IS NULL 

Когда я побежал это против моих таблиц он вернулся 38 строк. Этот номер помог нам определить, возвращал ли запрос, который мы написали, правильное количество строк. Ответ @Gordon Linoff отличался на 26 строк, поэтому я думаю, что он возвращал больше строк, чем ожидалось, но это было ближе, чем все остальные ответы и шаг в правильном направлении.

Затем мы собираем этот запрос, который использует CROSS APPLY (немного sql, которого я никогда раньше не видел).

Запрос выглядит следующим образом:

SELECT ad.* 
FROM Form f 
CROSS APPLY (SELECT TOP 1 * FROM FormDetails fd WHERE 
      f.form_id = fd.form_id AND f.status_id = fd.status_id) AS ad 

Когда я сравниваю количество строк, которые находятся в форме таблицы с количеством строк, возвращаемых этим запросом я получаю разница 38. Это, кажется, указывают на что это хорошее решение моей проблемы.

Я бы никогда не думал использовать CROSS APPLY, чтобы решить эту проблему ... но я думал, что буду документировать решение здесь на всякий случай, когда это поможет кому-то в будущем.

0

Вы можете использовать функцию row_number() SQL ANSI для перечисления строк в FormDetails для каждого form_id. Вам не все равно, что конкретный порядок, так что следующий должен делать то, что вы хотите:

select form_id, status_id, process_id 
from (select fd.*, row_number() over (partition by form_id order by form_id) as seqnum 
     from formdetails fd 
    ) fd 
where exists (select 1 from forms f where fd.form_id = f.form_id) and 
     seqnum = 1; 
+0

Это похоже на шаг в правильном направлении, но что-то не так. Когда я 'выбираю count (*) из forms', я получаю 21607. Когда я подсчитываю количество строк, возвращаемых вашим запросом, я получаю 21581. Это позволяет мне знать, что в вашем запросе выбрано 26 форм. – mezoid

+0

Я думаю, что проблема с этим запросом заключается в том, что он не дает последовательности из 1 детали формы, которая имеет статус, соответствующий статусу в таблице форм. Любая идея, как адаптировать ваш запрос, чтобы это разрешить? – mezoid

+0

@mezoid. , , Ваш вопрос начинает «выбирать все строки в FormDetails». В «Формах», которые не находятся в 'FormDetails', должно быть 26 форм. Если вы хотите, просто удалите предложение 'exist'. –

0

Используйте SELECT DISTINCT заявление

SELECT DISTINCT * 
FROM FormDetails fd 
RIGHT JOIN Form f ON f.form_id = fd.form_id AND f.status_id = fd.status_id 
0

Найти таблицу Distinct rows в Form затем используйте Right Outer join с Formdetails таблицей

SELECT f.form_id, 
     f.status_id, 
     f.process_id, 
FROM FormDetails fd 
     RIGHT JOIN (SELECT DISTINCT form_id, 
            status_id, 
            process_id, 
        FROM Form) f 
       ON f.form_id = fd.form_id 
        AND f.status_id = fd.status_id 
0

Вот что я сделал бы, чтобы решить вашу проблему.

1- Создайте новое поле, которое будет действовать как ваш уникальный ключ в таблице formDetails.

Alter Table formDetails Add formDetail_id int Not Null Identity(1,1) Constraint PKFormDetail Primary Key 

2- Тогда я создам временное поле, которое будет служить для записи оценки существующих данных. Я имею в виду, что временное поле, созданное (я назову его Eval), может содержать рассчитанное значение из ваших трех полей (form_id, status_id и process_id).

Alter Table formDetails Add Eval int 

Update formDetails Set Eval = form_id * 100000 + status_id * 1000 + process_id 

Какое бы решение ни взять, вы должны позаботиться о том, что результат не содержит «коллизий» (в зависимости от данных, содержащихся в таблице. Я имею в виду в этом случае PROCESS_ID должен быть меньше 1000 и status_id * 1000 должно быть менее 100 000).

3- Удалите ненужные строки, как это:

Delete x 
    FROM (select *, rn=row_number() over (partition by Eval order by formDetail_id) 
     from [dbo].[formDetails]) x 
    Where rn > 1; 

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

4- Удалить временное поле из таблицы.

Alter Table formDetails Drop Column Eval 

5- Теперь вы можете напрямую установить свой внешний ключ между полем form_id двух таблиц.

Alter Table formDetails Add Constraint FKFormId Foreign Key (form_id) references forms(form_id) 
0
CREATE TABLE #Form 
(
formId INT, 
StatusID INT, 
FormCreate VARCHAR(30) 
) 

INSERT INTO #Form VALUES(1,1,'abc') 
INSERT INTO #Form VALUES(2,3,'def') 

CREATE TABLE #Form1 
(
formId INT, 
StatusID INT, 
ProcessID INT 
) 

INSERT INTO #Form1 VALUES(1,1,1) 
INSERT INTO #Form1 VALUES(2,2,1) 
INSERT INTO #Form1 VALUES(2,3,1) 
INSERT INTO #Form1 VALUES(2,3,1) 

SELECT DISTINCT * FROM #Form fd 
LEFT JOIN #Form1 f ON f.formId = fd.formId AND f.StatusID = fd.StatusID 
+0

Ваше решение отлично работает, но не меняет таблицу formDetails. Но оттуда можно было бы полностью воссоздать таблицу formDetail. Так что это может сработать. –

Смежные вопросы