1

我很难将我想要的东西翻译成函数式编程,因为我认为是命令式的。基本上,我有一张表格和一张期望表。在期望视图中,我希望它查看表单表并告诉我是否每个人都找到了匹配项。但是,当我尝试使用连接来完成此操作时,当两个或多个表单匹配时,连接会将行添加到 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])

哪个工作正常,但速度很慢,每次有两个匹配项时,都会在期望结果中添加一行。从数学上讲,我知道连接是交叉乘法,这是意料之中的,但我不确定没有它们如何做到这一点。也许是子查询?

我无法提供有关实施的更多详细信息,但我很乐意尝试任何建议并用结果做出回应。我有 880 个期望行,并返回 942 个结果。如果我只允许匹配一种形式的结果,我会得到 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' 表明他们可能已经开始了一个但没有完成它。我的观点有四种不同的“匹配标准”——每一种都更严格一些,而且每一种都算数。'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 (table-expression) 指定了一个 table-expression,它为 FROM 子句的前面 table 表达式中的每一行调用一次,然后加入它们。然而,实际上,编译器将它几乎等同于 JOIN。

实际的区别在于,与 JOIN 表表达式不同,APPLY 表表达式会为每一行动态重新计算。因此,它不是使用 ON 子句,而是依靠自己的逻辑和 WHERE 子句来限制/匹配其行与前面的表表达式。这也允许它在自己的内部子查询表达式引用前面表表达式的列值。(这在 JOIN 中是不可能的)

我们在这里想要这个而不是 JOIN 的原因是我们需要在子查询中使用 TOP 1 来限制其返回的行,但是,这意味着我们需要将 ON 子句条件移动到内部 WHERE 子句所以它将在评估 TOP 1之前应用。意味着我们在这里需要一个 APPLY,而不是更常见的 JOIN。

于 2012-05-04T03:49:12.180 回答
1

@RBarryYoung 回答了我提出的问题,但还有第二个问题我没有说得很清楚。我真正想要的是他的回答和这个问题的结合,所以为了记录,这就是我使用的:

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 回答