1

メインテーブルに基本的な連絡先情報と電話番号サブテーブルが含まれるアドレス帳を作成しているとしましょう -

Contact
===============
Id         [PK]
Name

PhoneNumber
===============
Id         [PK]
Contact_Id [FK]
Number

したがって、Contact レコードは、PhoneNumber テーブルに 0 個以上の関連レコードを持つ場合があります。主キー以外の列の一意性に関する制約はありません。実際、次の理由により、これは真実でなければなりません。

  1. 名前が異なる 2 人の連絡先が電話番号を共有している可能性があります。
  2. 2 つの連絡先の名前が同じで、電話番号が異なる場合があります。

重複レコードを含む可能性のある大規模なデータセットをデータベースにインポートし、SQL を使用して重複を除外したいと考えています。重複レコードを識別するためのルールは単純です...それらは同じ名前を共有し、同じ内容を持つ同じ数の電話レコードを共有する必要があります。

もちろん、これは Contact テーブルから重複を選択するのに非常に効果的ですが、私のルールでは実際の重複を検出するのには役立ちません:

SELECT * FROM Contact
WHERE EXISTS
    (SELECT 'x' FROM Contact t2 
     WHERE t2.Name = Contact.Name AND
           t2.Id > Contact.Id);

私が欲しいものは、私がすでに持っているものの論理的な拡張であるように思えますが、私はそれを見落としているに違いありません. 何か助けはありますか?

ありがとう!

4

3 に答える 3

0

「持つ」というキーワードはあなたの友達です。一般的な用途は次のとおりです。

select field1, field2, count(*) records
from whereever
where whatever
group by field1, field2
having records > 1

having 句でエイリアスを使用できるかどうかは、データベース エンジンによって異なります。この基本原則を状況に適用できるはずです。

于 2013-09-30T19:43:35.267 に答える
0

著者は、「2 人が同一人物である」という要件を次のように述べています。

  1. 同姓同名で、
  2. 同じ数の電話番号を持ち、それらはすべて同じです。

したがって、問題は見た目よりも少し複雑です (または、単に考えすぎただけかもしれません)。

サンプルデータと(醜いものですが、一般的なアイデアはそこにあります)正しく動作しているように見える以下のテストデータでテストしたサンプルクエリ(私はOracle 11g R2を使用しています):

CREATE TABLE contact (
  id NUMBER PRIMARY KEY,
  name VARCHAR2(40))
;

CREATE TABLE phone_number (
  id NUMBER PRIMARY KEY,
  contact_id REFERENCES contact (id),
  phone VARCHAR2(10)
);

INSERT INTO contact (id, name) VALUES (1, 'John');
INSERT INTO contact (id, name) VALUES (2, 'John');
INSERT INTO contact (id, name) VALUES (3, 'Peter');
INSERT INTO contact (id, name) VALUES (4, 'Peter');
INSERT INTO contact (id, name) VALUES (5, 'Mike');
INSERT INTO contact (id, name) VALUES (6, 'Mike');
INSERT INTO contact (id, name) VALUES (7, 'Mike');

INSERT INTO phone_number (id, contact_id, phone) VALUES (1, 1, '123'); -- John having number 123
INSERT INTO phone_number (id, contact_id, phone) VALUES (2, 1, '456'); -- John having number 456

INSERT INTO phone_number (id, contact_id, phone) VALUES (3, 2, '123'); -- John the second having number 123
INSERT INTO phone_number (id, contact_id, phone) VALUES (4, 2, '456'); -- John the second having number 456

INSERT INTO phone_number (id, contact_id, phone) VALUES (5, 3, '123'); -- Peter having number 123
INSERT INTO phone_number (id, contact_id, phone) VALUES (6, 3, '456'); -- Peter having number 123
INSERT INTO phone_number (id, contact_id, phone) VALUES (7, 3, '789'); -- Peter having number 123

INSERT INTO phone_number (id, contact_id, phone) VALUES (8, 4, '456'); -- Peter the second having number 456

INSERT INTO phone_number (id, contact_id, phone) VALUES (9, 5, '123'); -- Mike having number 456
INSERT INTO phone_number (id, contact_id, phone) VALUES (10, 5, '456'); -- Mike having number 456

INSERT INTO phone_number (id, contact_id, phone) VALUES (11, 6, '123'); -- Mike the second having number 456
INSERT INTO phone_number (id, contact_id, phone) VALUES (12, 6, '789'); -- Mike the second having number 456

-- Mike the third having no number
COMMIT;

-- does not meet the requirements described in the question - will return Peter when it should not
SELECT DISTINCT c.name
  FROM contact c JOIN phone_number pn ON (pn.contact_id = c.id)
GROUP BY name, phone_number
HAVING COUNT(c.id) > 1
;

-- returns correct results for provided test data
-- take all people that have a namesake in contact table and
-- take all this person's phone numbers that this person's namesake also has
-- finally (outer query) check that the number of both persons' phone numbers is the same and
-- the number of the same phone numbers is equal to the number of (either) person's phone numbers
SELECT c1_id, name
  FROM (
    SELECT c1.id AS c1_id, c1.name, c2.id AS c2_id, COUNT(1) AS cnt
      FROM contact c1
        JOIN contact c2 ON (c2.id != c1.id AND c2.name = c1.name)
        JOIN phone_number pn ON (pn.contact_id = c1.id)
    WHERE
      EXISTS (SELECT 1
                FROM phone_number
              WHERE contact_id = c2.id
                AND phone = pn.phone)
    GROUP BY c1.id, c1.name, c2.id
  )
WHERE cnt = (SELECT COUNT(1) FROM phone_number WHERE contact_id = c1_id)
  AND (SELECT COUNT(1) FROM phone_number WHERE contact_id = c1_id) = (SELECT COUNT(1) FROM phone_number WHERE contact_id = c2_id)
;

-- cleanup
DROP TABLE phone_number;
DROP TABLE contact;

SQL Fiddle で確認してください: http://www.sqlfiddle.com/#!4/36cdf/1

編集済み

著者のコメントへの回答:もちろん、私はそれを考慮に入れていませんでした...ここに修正された解決策があります:

-- new test data
INSERT INTO contact (id, name) VALUES (8, 'Jane');
INSERT INTO contact (id, name) VALUES (9, 'Jane');

SELECT c1_id, name
  FROM (
    SELECT c1.id AS c1_id, c1.name, c2.id AS c2_id, COUNT(1) AS cnt
      FROM contact c1
        JOIN contact c2 ON (c2.id != c1.id AND c2.name = c1.name)
        LEFT JOIN phone_number pn ON (pn.contact_id = c1.id)
    WHERE pn.contact_id IS NULL
      OR EXISTS (SELECT 1
                FROM phone_number
              WHERE contact_id = c2.id
                AND phone = pn.phone)
    GROUP BY c1.id, c1.name, c2.id
  )
WHERE (SELECT COUNT(1) FROM phone_number WHERE contact_id = c1_id) IN (0, cnt)
  AND (SELECT COUNT(1) FROM phone_number WHERE contact_id = c1_id) = (SELECT COUNT(1) FROM phone_number WHERE contact_id = c2_id)
;

電話番号が存在しない状況 (LEFT JOIN) を許可し、外側のクエリで個人の電話番号の数を比較します。これは 0 に等しいか、内側のクエリから返された数のいずれかでなければなりません。

于 2013-09-30T20:43:11.333 に答える