1

私は命令的に考えるので、私が望むものを関数型プログラミングに変換するのに苦労しています。基本的に、フォームの表と期待値の表があります。Expectation ビューでは、forms テーブルを調べて、それぞれが一致するかどうかを教えてくれるようにしたいと考えています。ただし、これを実現するために結合を使用しようとすると、2 つ以上のフォームが一致すると、結合によって Expectation テーブルに行が追加されます。これいらない。

命令的な方法で、私はこれと同等のものを求めています:

ForEach (row in Expectation table)
{
    if (any form in the Form table matches the criteria)
    {
         MatchID = form.ID;
         SignDate = form.SignDate;
         ...
    }
}

私がSQLで持っているのはこれです:

SELECT
    e.*, match.ID, match.SignDate, ...
FROM
   POFDExpectation e LEFT OUTER JOIN
   (SELECT MIN(ID) as MatchID, MIN(SignDate) as MatchSignDate, 
        COUNT(*) as MatchCount, ...
    FROM Form f
    GROUP BY (matching criteria columns)
        ) match
        ON (form.[match criteria] = expectation.[match criteria])

これは正常に機能しますが、非常に遅く、2 つの一致があるたびに、Expectation の結果に行が追加されます。数学的には、結合がクロス乗算であり、これが予想されることを理解していますが、それらなしでこれを行う方法がわかりません。おそらくサブクエリ?

実装についてこれ以上詳しく説明することはできませんが、喜んで提案を試し、結果を返信します。880 の期待行があり、942 の結果が返されます。1 つのフォームに一致する結果のみを許可すると、831 件の結果が得られます。どちらも望ましいものではないので、あなたの回答が正確に 880 に到達した場合、あなたの回答が受け入れられます。

編集: SQL Server 2008 R2 を使用していますが、一般的なソリューションが最適です。

サンプルコード:

--DROP VIEW ExpectationView; DROP TABLE Forms; DROP TABLE Expectations;
--Create Tables and View
CREATE TABLE Forms (ID int IDENTITY(1,1) PRIMARY KEY, ReportYear int, Name varchar(100), Complete bit, SignDate datetime)
GO
CREATE TABLE Expectations (ID int IDENTITY(1,1) PRIMARY KEY, ReportYear int, Name varchar(100))
GO
CREATE VIEW ExpectationView AS select e.*, filed.MatchID, filed.SignDate, ISNULL(filed.FiledCount, 0) as FiledCount, ISNULL(name.NameCount, 0) as NameCount from Expectations e LEFT OUTER JOIN 
(select MIN(ID) as MatchID, ReportYear, Name, Complete, Min(SignDate) as SignDate, COUNT(*) as FiledCount from Forms f GROUP BY ReportYear, Name, Complete) filed
on filed.ReportYear = e.ReportYear AND filed.Name like '%'+e.Name+'%' AND filed.Complete = 1 LEFT OUTER JOIN 
(select MIN(ID) as MatchID, ReportYear, Name, COUNT(*) as NameCount from Forms f GROUP BY ReportYear, Name) name 
on name.ReportYear = e.ReportYear AND name.Name like '%'+e.Name+'%'
GO
--Insert Text Data
INSERT INTO Forms (ReportYear, Name, Complete, SignDate)
SELECT 2011, 'Bob Smith', 1, '2012-03-01' UNION ALL
SELECT 2011, 'Bob Jones', 1, '2012-10-04' UNION ALL
SELECT 2011, 'Bob', 1, '2012-07-20'
GO
INSERT INTO Expectations (ReportYear, Name)
SELECT 2011, 'Bob'
GO
SELECT * FROM ExpectationView --Should only return 1 result, returns 9

'filed' はフォームを完了したことを示し、'name' はフォームを開始したが完了していない可能性があることを示します。私の見解には 4 つの異なる「一致基準」があります。それぞれがもう少し厳密で、それぞれをカウントします。'Name Only Matches'、'Loose Matches'、'Matches' (デフォルト)、'Tight Matches' (複数のデフォルト マッチがある場合に使用されます。

4

2 に答える 2

6

これは、JOINタイプのクエリ形式を維持したい場合の方法です。

SELECT
    e.*, 
    match.ID, 
    match.SignDate, 
    ...
FROM        POFDExpectation e 
OUTER APPLY (
    SELECT  TOP 1
        MIN(ID) as MatchID, 
        MIN(SignDate) as MatchSignDate, 
        COUNT(*) as MatchCount, 
        ...
    FROM    Form f
    WHERE   form.[match criteria] = expectation.[match criteria] 
    GROUP BY ID (matching criteria columns)   
    -- Add ORDER BY here to control which row is TOP 1
    ) match

通常はパフォーマンスも向上します。


意味的には、{CROSS|OUTER} APPLY (テーブル式) は、FROM 句の前のテーブル式の各行に対して 1 回呼び出され、それらに結合されるテーブル式を指定します。ただし、実用的には、コンパイラはこれを JOIN とほぼ同じように扱います。

実際の違いは、JOIN table-expression とは異なり、APPLY table-expression は行ごとに動的に再評価されることです。したがって、ON 句の代わりに、独自のロジックと WHERE 句に依存して、行を前のテーブル式に制限/一致させます。これにより、独自の内部サブクエリ式で、前のテーブル式の列値を参照することもできます。(これは JOIN では不可能です)

ここで JOIN の代わりにこれが必要な理由は、返される行を制限するためにサブクエリで TOP 1 が必要だからです。ただし、これは、ON 句の条件を内部の WHERE 句に移動する必要があることを意味します。TOP 1 が評価されるに適用されること。つまり、通常の JOIN ではなく、ここで APPLY が必要です。

于 2012-05-04T03:49:12.180 に答える
1

@RBarryYoung は、私が尋ねたとおりに質問に答えましたが、2 番目の質問があり、あまり明確にしませんでした。私が本当に欲しかったのは、彼の答えとこの質問の組み合わせだったので、記録のために私が使用したものは次のとおりです。

SELECT
    e.*, 
     ...
    match.ID, 
    match.SignDate, 
    match.MatchCount
FROM 
    POFDExpectation e 
    OUTER APPLY (
        SELECT  TOP 1
            ID as MatchID, 
            ReportYear,
             ...
            SignDate as MatchSignDate, 
            COUNT(*) as MatchCount OVER ()
        FROM
            Form f
        WHERE
            form.[match criteria] = expectation.[match criteria]
        -- Add ORDER BY here to control which row is TOP 1
        ) match
于 2012-05-04T17:07:19.597 に答える