0

3 つの入力フィールドを持つ検索があります (引数のために、LastName、Last4Ssn、DateOfBirth としましょう)。これら 3 つの入力フィールドは、ユーザーがこれら 3 つのフィールドの 1 つ以上の組み合わせを検索するために選択できる動的グリッドにあります。たとえば、ユーザーは次の表現に基づいて検索する場合があります。

LastName    Last4Ssn    DateOfBirth
--------    --------    -----------
Smith       NULL        1/1/1970
Smithers    1234        NULL
NULL        5678        2/2/1980

この例では、最初の行は LastName と DateOfBirth による検索を表しています。2 つ目は、LastName と Last4Ssn によるものです。そして、3 つ目は、Last4Ssn と DateOfBirth によるものです。実際のシナリオには 4 つのフィールドがあるため、この例は少し不自然です。少なくとも 2 つのフィールドに検索データを入力する必要があり (検証方法は気にしないでください)、すべてのフィールドが入力されている可能性があります。

カーソルを使用せずに、各行の指定された値をフィルターとして使用して、そのデータを既存のテーブルに結合するにはどうすればよいでしょうか? 現在、上記のテーブルの各行を通過し、値を持つ列に基づいて結合を実行し、見つかったデータを一時テーブルに挿入するカーソルがあります。このようなもの:

CREATE TABLE #results (
    Id INT,
    LastName VARCHAR (26),
    Last4Ssn VARCHAR (4),
    DateOfBirth DATETIME
)

DECLARE @lastName VARCHAR (26)
DECLARE @last4Ssn VARCHAR (4)
DECLARE @dateOfBirth DATETIME

DECLARE search CURSOR FOR
    SELECT LastName, Last4Ssn, DateOfBirth
    FROM #searchData

OPEN search
    FETCH NEXT FROM search
    INTO @lastName, @last4Ssn, @dateOfBirth

    WHILE @@FETCH_STATUS = 0
    BEGIN           
        INSERT INTO #results
            SELECT s.Id, s.LastName, s.Last4Ssn, s.DateOfBirth
            FROM SomeTable s
            WHERE Last4Ssn = ISNULL(@last4Ssn, Last4Ssn)
                AND DateOfBirth = ISNULL(@dateOfBirth, DateOfBirth)
                AND (
                    LastName = ISNULL(@lastName, LastName)
                    OR LastName LIKE @lastName + '%'
                )
        FETCH NEXT FROM search
        INTO @lastName, @last4Ssn, @dateOfBirth
    END
CLOSE search
DEALLOCATE search

コードをもう少し読みやすくするために、カーソルを回避する方法があることを望んでいました。検索に使用されるテーブルには 5 ~ 10 を超えるレコードが含まれることはないため、パフォーマンスは問題ではありませんが、少数を超える場合は、1 つではなく一度にデータをクエリする方が効率的だと思います。一列ずつ。このSomeData例のテーブルは非常に大きくなる可能性があります。

4

1 に答える 1

1

2 つのテーブルを結合できない理由がわかりません。

CREATE TABLE #results (
    Id INT,
    LastName VARCHAR (26),
    Last4Ssn VARCHAR (4),
    DateOfBirth DATETIME
)

INSERT INTO #results
select s.id, s.lastname, s.last4ssn, s.dateofbirth
from SomeTable s
join #searchData d
    ON s.last4ssn = isnull(d.last4ssn, s.last4ssn)
    AND s.dateofbirth = isnull(d.dateofbirth, s.dateofbirth)
    AND (s.lastname = isnull(d.lastname, s.lastname) OR
        OR s.lastname like d.lastname + '%')

編集:

データが大きいため、適切なインデックスが必要になります。実質的に 3 つの句を OR で結合しているため、1 つのインデックスでは十分ではありません。したがって、最初のステップはこれらのインデックスを作成することです。

CREATE TABLE SomeData (
    Id INT identity(1,1),
    LastName VARCHAR (26),
    Last4Ssn VARCHAR (4),
    DateOfBirth DATETIME
)

create nonclustered index ssnlookup on somedata (last4ssn)
create nonclustered index lastnamelookup on somedata (lastname)
create nonclustered index doblookup on somedata (dateofbirth)

次のステップでは、これらのインデックスを使用するクエリを作成します。ここで何が最善の方法かはわかりませんが、4 つのクエリを結合することだと思います。

with searchbyssn as (
  select somedata.* from somedata join #searchData 
    on somedata.last4ssn = #searchData.last4ssn
), searchbyexactlastname as (
  select somedata.* from somedata join #searchData
    on somedata.lastname = #searchData.lastname
), searchbystartlastname as (
  select somedata.* from somedata join #searchData
    on somedata.lastname like #searchdata.lastname + '%'
), searchbydob as (
  select somedata.* from somedata join #searchData 
    on somedata.dateofbirth = #searchData.dateofbirth
), s as (
  select * from searchbyssn
  union select * from searchbyexactlastname
  union select * from searchbystartlastname
  union select * from searchbydob
)
select s.id, s.lastname, s.last4ssn, s.dateofbirth
from s
join #searchData d
   ON (d.last4ssn is null or s.last4ssn = d.last4ssn)
   AND s.dateofbirth = isnull(d.dateofbirth, s.dateofbirth)
   AND (s.lastname = isnull(d.lastname, s.lastname)
       OR s.lastname like d.lastname + '%')

これは、4 つのインデックス シークを示すフィドルです: http://sqlfiddle.com/#!6/3741d/3

ユニオンのかなりのリソース使用量を示していますが、大きなテーブルのインデックス スキャンと比較すると無視できると思います。数百行を超えるサンプル データを生成することはできません。結果の行数が少ないため、最後に #searchData に結合してすべての結果を再度フィルター処理するのはコストがかかりません。

于 2013-06-21T04:02:00.013 に答える