2009-07-02 33 views
2

候補重複のリストを作成するためのこのクエリは、十分に簡単です:TSQLグループBy "OR"?

SELECT Count(*), Can_FName, Can_HPhone, Can_EMail 
FROM Can 
GROUP BY Can_FName, Can_HPhone, Can_EMail 
HAVING Count(*) > 1 

しかし、私は反対確認したい、実際のルールがします。FNameと(HPhoneまたは電子メール)であれば - どのように私は仕事に、GROUP BYを調整することができますこれとともに?

私はここでUNION SELECTで終わるつもりです(つまり、FName、HPhoneを1つ、FName、EMailをもう一方にして結果を結合する) - しかし、誰もが簡単な方法を知っています。

ご協力いただきありがとうございます。

スコットはメイン州で

+0

「Bob/anything/[email protected]」と「Bob/567/[email protected]」の両方が重複としてカウントされるようにしますか? 'Bob/234/[email protected]'があればどうなりますか?グループ化ルールを明確にするためのデータの例が実際に役立ちます。 – AakashM

+0

理解しやすい。申し訳ありません。 Quassnoiは下の良い例を与えました(そして私はそこに質問に答えました)。ありがとうございました。 – Scott04073

+0

重複を実際に削除しますか? –

答えて

0

どれも正しくありません。 Quassnoi'sはまともなアプローチですが、 "qo.id> dup.id"と "di.chainid < do.chainid"の表現には致命的な欠陥が1つあります。 IDの固有の順序付けに依存するため、これは常に悪い習慣です。 IDには絶対に暗黙の意味を与えるべきではなく、等価性テストまたはヌルテストにのみ参加する必要があります。この例でQuassnoiのソリューションを簡単に破るには、単にデータのIDを並べ替えるだけです。

本質的な問題は、2つのレコードが中間体を介して関連している可能性を導く、グルーピングを伴う分離条件です。ただし、それらは直接関連性がありません。 (1)ジョン・555-00-00 [email protected]

(2)ジョン・555-00-01ジョン@例:

例えば、あなたはこれらのレコードは、すべてのグループ化されなければならないと述べました。 comの

(3)ジョン・555-00-01 [email protected]

あなたは#2、#3、しかしはっきり#1とされているよう#1、#2は、関係づけされていることがわかります#3は、グループとして直接関連付けることはできません。

これは、再帰的または反復的な解決策が唯一可能な解決策であることを確立します。

ループの状況で簡単に終了できるので、再帰は実行可能ではありません。これはQuassnoiがID比較で避けようとしていたものですが、そうすることでアルゴリズムを壊しました。あなたは再帰のレベルを制限しようとする可能性がありますが、すべての関係を完了することはできませんが、潜在的に自分自身にループを追いかけてしまうため、データサイズが過大になり、非効率的になります。

最適解はITERATIVEです。各IDを一意のグループIDとしてタグ付けして結果セットを開始し、結果セットを回転して更新し、IDを結合論理条件と一致する同じ一意のグループIDに結合します。それ以上の更新が行われなくなるまで、毎回更新されたセットに対してこのプロセスを繰り返します。

私はすぐにこのためのサンプルコードを作成します。

+0

クエリのスコープ内で、IDは安定しています。それらが同等か存在しない場合は、生成された 'ROW_NUMBER'で置き換えることができます。 – Quassnoi

+0

基本的に私がやったこと。私はいずれかの側面をグループ化し、DB機能を介して完全な比較を行った。 – Scott04073

0

GROUP BYは対応していない - それは暗黙的だとし、選択リスト内のすべての非アグリゲータを含める必要があります。

+0

UserOR(hphone、email)などのユーザー定義関数で何かをハッキングし、GROUP BY –

+0

btwに含めると、選択リストとグループ句に含める必要があります。そして、関数は決定論的でなければならないが、ORは決定論的なものである。 –

+0

@Arnshea:ORは3つの結果を与えることができるので、最大3つのグループになる。 – Quassnoi

3

私は何を助言することができます前に、私はこの質問への答えを知っておく必要があります。

name phone  email 

John 555-00-00 [email protected] 
John 555-00-01 [email protected] 
John 555-00-01 [email protected] 

あなたは、このデータのために何をしたいですCOUNT(*)

更新:

あなただけのレコードがどんな重複を持っていることを知りたい場合は、この使用します。それは、より効率的だが、どこの重複を教えてくれていません

WITH q AS (
     SELECT 1 AS id, 'John' AS name, '555-00-00' AS phone, '[email protected]' AS email 
     UNION ALL 
     SELECT 2 AS id, 'John', '555-00-01', '[email protected]' 
     UNION ALL 
     SELECT 3 AS id, 'John', '555-00-01', '[email protected]' 
     UNION ALL 
     SELECT 4 AS id, 'James', '555-00-00', '[email protected]' 
     UNION ALL 
     SELECT 5 AS id, 'James', '555-00-01', '[email protected]' 
     ) 
SELECT * 
FROM q qo 
WHERE EXISTS 
     (
     SELECT NULL 
     FROM q qi 
     WHERE qi.id <> qo.id 
       AND qi.name = qo.name 
       AND (qi.phone = qo.phone OR qi.email = qo.email) 
     ) 

をチェーンが始まった。

このクエリは、重複チェーンが開始された場所を示す特別なフィールドchainidとともに、すべてのエントリを選択します。

WITH q AS (
     SELECT 1 AS id, 'John' AS name, '555-00-00' AS phone, '[email protected]' AS email 
     UNION ALL 
     SELECT 2 AS id, 'John', '555-00-01', '[email protected]' 
     UNION ALL 
     SELECT 3 AS id, 'John', '555-00-01', '[email protected]' 
     UNION ALL 
     SELECT 4 AS id, 'James', '555-00-00', '[email protected]' 
     UNION ALL 
     SELECT 5 AS id, 'James', '555-00-01', '[email protected]' 
     ), 
     dup AS (
     SELECT id AS chainid, id, name, phone, email, 1 as d 
     FROM q 
     UNION ALL 
     SELECT chainid, qo.id, qo.name, qo.phone, qo.email, d + 1 
     FROM dup 
     JOIN q qo 
     ON  qo.name = dup.name 
       AND (qo.phone = dup.phone OR qo.email = dup.email) 
       AND qo.id > dup.id 
     ), 
     chains AS 
     (
     SELECT * 
     FROM dup do 
     WHERE chainid NOT IN 
       (
       SELECT id 
       FROM dup di 
       WHERE di.chainid < do.chainid 
       ) 
     ) 
SELECT * 
FROM chains 
ORDER BY 
     chainid 
+0

定義によれば、それは3のCount(*)です。したがって、複雑さです。ありがとうございました。 – Scott04073

+0

実際にはもう一度見てみましょう。すぐ戻ってきます。 – Scott04073

+0

OK - 間違いなく3.それを再度確認しなければなりませんでした。 – Scott04073

0

また、このテーブルのプライマリキーとして一意のID整数があるとします。そうでない場合は、この目的のために、そして他の多くの目的のために、それを持っていることをお勧めします。

自己結合によってそれらの重複を検索:クエリはあなたに、各Nの重複組み合わせのためのN-1行を与える

select 
    c1.ID 
, c1.Can_FName 
, c1.Can_HPhone 
, c1.Can_Email 
, c2.ID 
, c2.Can_FName 
, c2.Can_HPhone 
, c2.Can_Email 
from 
(
    select 
     min(ID), 
     Can_FName, 
     Can_HPhone, 
     Can_Email 
    from Can 
    group by 
     Can_FName, 
     Can_HPhone, 
     Can_Email 
) c1 
inner join Can c2 on c1.ID < c2.ID 
where 
    c1.Can_FName = c2.Can_FName 
and (c1.Can_HPhone = c2.Can_HPhone OR c1.Can_Email = c2.Can_Email) 
order by 
    c1.ID 

- あなたはそれぞれのユニークな組み合わせと一緒にだけ、カウントをしたい場合は、グループ化された行を数えます「左」側で:確か

select count(1) + 1, 
, c1.Can_FName 
, c1.Can_HPhone 
, c1.Can_Email 
from 
(
    select 
     min(ID), 
     Can_FName, 
     Can_HPhone, 
     Can_Email 
    from Can 
    group by 
     Can_FName, 
     Can_HPhone, 
     Can_Email 
) c1 
inner join Can c2 on c1.ID < c2.ID 
where 
    c1.Can_FName = c2.Can_FName 
and (c1.Can_HPhone = c2.Can_HPhone OR c1.Can_Email = c2.Can_Email) 
group by 
    c1.Can_FName 
, c1.Can_HPhone 
, c1.Can_Email 

、これは労働組合よりも複雑である - 私はそれが重複について考えるための良い方法を示していると思います。

0

プロジェクト派生テーブルからの最初の所望の変換、その後、凝集を実行します。派生テーブルのプロジェクトリストで、必要に応じて

SELECT COUNT(*) 
    , CAN_FName 
    , Can_HPhoneOrEMail 
    FROM (
     SELECT Can_FName 
      , ISNULL(Can_HPhone,'') + ISNULL(Can_EMail,'') AS Can_HPhoneOrEMail 
     FROM Can) AS Can_Transformed 
    GROUP BY Can_FName, Can_HPhoneOrEMail 
    HAVING Count(*) > 1 

は、あなたの「OR」操作を調整します。

+0

これはあなたが "OR"よりもむしろ記述しているAND状況のようです - しかし、努力してくれてありがとう。 – Scott04073

0

私はこの答えは、一時テーブルの使用のために批判されますが、それはとにかく動作します知っている:

-- create temp table to give the table a unique key 
create table #tmp(
ID int identity, 
can_Fname varchar(200) null, -- real type and len here 
can_HPhone varchar(200) null, -- real type and len here 
can_Email varchar(200) null, -- real type and len here 
) 

-- just copy the rows where a duplicate fname exits 
-- (better performance specially for a big table) 
insert into #tmp 
select can_fname,can_hphone,can_email 
from Can 
where can_fname exists in (select can_fname from Can 
group by can_fname having count(*)>1) 

-- select the rows that have the same fname and 
-- at least the same phone or email 
select can_Fname, can_Hphone, can_Email 
from #tmp a where exists 
(select * from #tmp b where 
a.ID<>b.ID and A.can_fname = b.can_fname 
and (isnull(a.can_HPhone,'')=isnull(b.can_HPhone,'') 
or (isnull(a.can_email,'')=isnull(b.can_email,'')) 
+0

時々一時テーブルが問題を解決するために最良であるか、(まれにしか)方法ではありません。 – RolandTumble

0

はこれを試してみてください。これらの答えの

SELECT Can_FName, COUNT(*) 
FROM (
SELECT 
rank() over(partition by Can_FName order by Can_FName,Can_HPhone) rnk_p, 
rank() over(partition by Can_FName order by Can_FName,Can_EMail) rnk_m, 
Can_FName 
FROM Can 
) X 
WHERE rnk_p=1 or rnk_m =1 
GROUP BY Can_FName 
HAVING COUNT(*)>1