3

初期データは次のとおりです。

CREATE TABLE #data
    (
    Id integer,
    Surname varchar(50),
    DOB datetime
    )   
INSERT INTO #data
values 
(1,'smith', null),
(2,'jones', '01 jan 1970'),
(3,'vernon', null),
(4,'smith', '01 jan 1970'),
(5,'jones', '01 jan 1970'),
(6,'vernon', '01 jan 1970'),
(7,null, '01 jan 1970') 

除外のリストは次のとおりです。

CREATE TABLE #exclusions
    (
    ExcludedSurname varchar(50),
    ExcludedDOB datetime
    )   
INSERT INTO #exclusions
values 
('smith', '01 jan 1970'),
('jones', '01 jan 1970'),
('vernon', null),
(null, '01 jan 1970')   

これが私の少し予想外の結果を返すクエリです:

SELECT * 
FROM #data a
WHERE
  NOT EXISTS
    (
    SELECT 1
    FROM #exclusions e
    WHERE
            a.DOB = e.ExcludedDOB and
            a.Surname = e.ExcludedSurname
    ) 

s 3と7を確実Idに除外するために、この醜いスクリプトの変更を行うことができます。本番テーブルには多くの可能なデータがあります(#dataのライブバージョンは1000mレコードです)-そのため、私はに代わるものをこれほどまでに選びましたnull

SELECT * 
FROM #data a
WHERE
  NOT EXISTS
    (
    SELECT 1
    FROM #exclusions e
    WHERE  
            ISNULL(a.DOB, '01 JAN 2200') = ISNULL(e.ExcludedDOB, '01 JAN 2200')  and
            ISNULL(a.Surname,'AAAAAAAAAAAAAAAA') = ISNULL(e.ExcludedSurname,'AAAAAAAAAAAAAAAA')
    )   

これはSQLフィドルにあります

上記を行うためのよりエレガントな方法はありますか?

4

3 に答える 3

6

PostgreSQL(SQL Fiddle)では、使用できます

WHERE (a.DOB, a.Surname) IS NOT DISTINCT FROM (e.ExcludedDOB, e.ExcludedSurname)

しかし、SQL Server には、これを機能させるための 2 つの項目がありません。行値コンストラクターIS [NOT] DISTINCT FROM

その間、ここからテクニックを使用できます:文書化されていないクエリ プラン: 等価比較

SELECT *
FROM   #data a
WHERE  NOT EXISTS (SELECT *
                   FROM   #exclusions e
                   WHERE  EXISTS (SELECT a.DOB,
                                         a.Surname
                                  INTERSECT
                                  SELECT e.ExcludedDOB,
                                         e.ExcludedSurname)) 

上記の用途のバリエーションEXCEPT(実例はこちら):

SELECT *
FROM   #data a
WHERE  EXISTS (SELECT a.DOB, 
                      a.Surname
               EXCEPT
               SELECT e.ExcludedDOB, 
                      e.ExcludedSurname 
               FROM #exclusions e)  
于 2013-01-05T15:01:13.707 に答える
2

@MartinSmith

この代替案をチェックしてください:

SELECT *
FROM   #data a
WHERE  EXISTS(SELECT a.Surname,
                     a.DOB
              EXCEPT
              SELECT e.ExcludedSurname,
                     e.ExcludedDOB
              FROM   #exclusions e) 

非常にエレガントで読みやすい。


なぜEXCEPTがすべての仕事をするように見えるのに、なぜ上記のEXISTSを気にするのですか?

SELECT Surname,
       DOB
FROM   #data
EXCEPT
SELECT ExcludedSurname,
       ExcludedDOB
FROM   #exclusions 

[私の友人に提供しました - 残念ながら彼は貢献していませんSO- 私が知っていることではありません]

両方の選択肢はSQL FIDDLEにあります

于 2013-01-07T16:06:43.897 に答える
1

除外に重複がない場合は、これleft outer joinを少なくともきれいに見えるように表現しないでください。

SELECT * 
FROM #data a left outer join
     #exclusions e
     on a.DOB = e.ExcludedDOB and
        a.Surname = e.ExcludedSurname
where e.ExcludedDOB is NULL and e.ExcludedSurname is null

coalesce次に、またはロジックを使用して NULL を処理できます(この例では両方を示しています)。

SELECT * 
FROM #data a left outer join
     #exclusions e
     on (a.DOB = e.ExcludedDOB or a.DOB is NULL and e.ExcludedDOB is NULL) and
        (coealesce(a.Surname, '<null>') = coalesce(e.ExcludedSurname, '<null>')
where e.ExcludedDOB is NULL and e.ExcludedSurname is null

これらすべてのアプローチの欠点は、除外テーブルのインデックスを利用するとは思わないことです。. . テーブルが大きい場合、これは良い考えです。1 つのアプローチでは 2 つの結合が必要ですが、この問題は解決されます。

SELECT * 
FROM #data a left outer join
     #exclusions e
     on a.DOB = e.ExcludedDOB and
        a.Surname = e.ExcludedSurname left outer join
     #exclusions enull
     on enull.ExcludedSurname is null and a.Surname is NULL and
        enull.ExcludedDOB = a.DOB
where e.ExcludedDOB is NULL and e.ExcludedSurname is null and
      enull.ExcludedDOB is NULL and enull.ExcludedSurname is null

ただし、Martin のアプローチは依然として最高のパフォーマンスを発揮する可能性があります。

idfrom #dataが必要ない場合、最も簡単な方法は次のとおりです。

select Surname, Dob
from #data
except (select ExcludedSurname, ExcludedDB from #exceptions)

私はこの構造を表の比較によく使用します。ただし、ID を取得するには、結合に NULL が含まれるという問題が残ります。

于 2013-01-05T15:26:42.953 に答える