1対多の関係を持つ2つのテーブル、PeopleとAttributesがあるとします。 first_name、last_nameに基づいて重複を見つけようとしています。すべての属性が正確に一致する必要があります。
CREATE TABLE People (Id int,
first_name varchar(100),
last_name varchar(100));
CREATE TABLE Attributes (Id int,
person_id int,
field varchar(100),
field_value varchar(100));
INSERT INTO People VALUES (1, 'John', 'Smith');
INSERT INTO People VALUES (2, 'John', 'Smith');
INSERT INTO People VALUES (3, 'John', 'Smith');
INSERT INTO Attributes VALUES (1, 1, 'HairColor', 'Brown');
INSERT INTO Attributes VALUES (2, 1, 'EyeColor', 'Blue');
INSERT INTO Attributes VALUES (3, 2, 'HairColor', 'Brown');
INSERT INTO Attributes VALUES (4, 2, 'EyeColor', 'Blue');
INSERT INTO Attributes VALUES (5, 3, 'HairColor', 'Blonde');
それは私たちに与えます:
id | first_name | last_name
----+------------+-----------
1 | John | Smith
2 | John | Smith
3 | John | Smith
id | person_id | field | field_value
----+-----------+-----------+-------------
1 | 1 | HairColor | Brown
2 | 1 | EyeColor | Blue
3 | 2 | HairColor | Brown
4 | 2 | EyeColor | Blue
5 | 3 | HairColor | Blonde
PeopleテーブルからID 1および2を返すクエリが必要です。 1つのテーブル内で重複を見つけることができます。
select first_name,last_name,count(*) from People
group by first_name,last_name having ( count(*) > 1 );
しかし、私は1対多のテーブルを結合し、両方のテーブル間で重複を検出することに問題があります。 1対多の関係を持つテーブル間で重複を検出するにはどうすればよいですか?
これを行う1つの方法( SQLfiddle を確認してください):
select
p1.id as id1,
p2.id as id2
from people p1
join people p2
on p1.first_name = p2.first_name
and p1.last_name = p2.last_name
and p1.id < p2.id
where not exists
( select 1
from
( select *
from attributes a1
where a1.person_id = p1.id
union all
select *
from attributes a2
where a2.person_id = p2.id
) g
group by field, field_value
having count(*) <> 2
) ;
そしてもう一つ:
select
p1.id as id1,
p2.id as id2
from people p1
join people p2
on p1.first_name = p2.first_name
and p1.last_name = p2.last_name
and p1.id < p2.id
where not exists
( ( select field, field_value
from attributes a1
where a1.person_id = p1.id
union
select field, field_value
from attributes a2
where a2.person_id = p2.id
)
except
( select field, field_value
from attributes a1
where a1.person_id = p1.id
intersect
select field, field_value
from attributes a2
where a2.person_id = p2.id
)
) ;
少なくともPostgresとSQL Serverでは、Intersectがexcept/minusよりも優先されます。安全のために、かっこを使用して優先順位を確認できます。
T-SQL構文を使用したテイクを次に示します。本当に標準の構文が必要な場合は、LIMIT 1
ではなくTOP 1
を使用するように変更できます。これが役に立てば幸いですが、それは ypercubeがカバーしています のように見えます。
SELECT TOP 1
MIN(p.Id) AS id1,
MAX(a.person_id) AS id2
FROM dbo.People AS p
CROSS JOIN dbo.Attributes AS a
WHERE p.Id = a.person_id
GROUP BY p.first_name ,
p.last_name ,
a.field ,
a.field_value
HAVING COUNT(*) = ( SELECT COUNT(a2.person_id)
FROM dbo.Attributes AS a2
WHERE a2.field = a.field
AND a2.field_value = a.field_value
);
属性が多かれ少なかれ修正されており、属性リストが変更されたときにコードリリースを実行してもかまわない場合は、JOINとCOUNTを使用してそれらをアンロールすると、それも実行されます。
select
p.Id,
a1.field_value,
a2.field_value
from People as p
left outer join Attributes as a1
on a1.person_id = p.Id
and a1.field = 'HairColor'
left outer join Attributes as a2
on a2.person_id = p.Id
and a2.field = 'EyeColor'
group by
p.Id,
a1.field_value,
a2.field_value
having
count(*) > 1;
効果的ですが、エレガントではありません。多くの人と属性を持っている場合は遅くなる可能性があります。