如何在左外连接中为每个键获得一行

时间:2016-10-06 14:03:03

标签: sql-server tsql sql-server-2008-r2

上下文是一个包含date和UserAccount的事务表。该表包含大约十亿行。

dOperationValueDate  sUserAccount                                  
-------------------  ----------------------------------------------
2016-03-05           00000000001                                   
2016-03-06           00000000002                                   
2016-03-07           00000000003                                   
2016-03-08           00000000004                                   
2016-03-09           00000000005                                   
2016-04-05           00000000002                                   
2016-10-05           00000000001                                   
2016-10-06           00000000001                                   
2016-10-06           00000000005                                   

我想在我的表格中找到这些标准的数据:

  • 6个月前至少有一笔交易(如TOP 1 *)
  • 6个月没有交易

在我的示例中,结果将是帐户2,3,4。

我开始使用LEFT OUTER JOIN,以便在6个月后删除所有带有事务的userId。但是处理时间非常糟糕:现在是4个小时。

SELECT b.sUserAccount FROM
(SELECT sUserAccount FROM T_Operations WITH (readuncommitted) WHERE dOperationValueDate < DATEADD(month, -6, DATEADD(month, DATEDIFF(month, 0, GETUTCDATE()), 0)) GROUP BY sUserAccount) b -- all operations before 6 months ago
LEFT JOIN
(SELECT sUserAccount FROM T_Operations WITH (readuncommitted) WHERE dOperationValueDate >= DATEADD(month, -6, DATEADD(month, DATEDIFF(month, 0, GETUTCDATE()), 0)) GROUP BY sUserAccount) c -- all operations since 6 months
ON b.sUserAccount = c.sUserAccount
WHERE c.sUserBankAccount IS NULL) d -- remove all customers who have operations before 6 months ago and since 6 months / keep only customers who have operations beofre 6 months ago only

我认为解决方案是在b查询中只找到一个操作,并且sql在找到一行时停止。主要问题是,如果用户在6个月之前没有交易,但对于其他用户,则会没事。

另一方面,我必须在6个月后检查每笔交易,以便将客户从范围中删除。

我读过CROSS APPLY,但我不确定它是如何工作的。

这里的主要问题是处理时间。我必须做一个“快速”请求(不到1小时)。

2 个答案:

答案 0 :(得分:0)

我认为你应该可以在这里使用NOT EXISTS。

SELECT  b.sUserAccount
FROM    T_Operations b WITH (READUNCOMMITTED)
WHERE   b.dOperationValueDate < DATEADD(month,-6,DATEADD(month,DATEDIFF(month,0,GETUTCDATE()),0))
        AND NOT EXISTS ( SELECT 1
                         FROM   T_Operations WITH (READUNCOMMITTED)
                         WHERE  sUserAccount = b.sUserAccount
                                AND dOperationValueDate >= DATEADD(month,-6,DATEADD(month,DATEDIFF(month,0,GETUTCDATE()),0)) )
GROUP BY b.sUserAccount -- all operations before 6 months ago

或者实际上,您可以将GROUP BY与HAVING一起使用

SELECT  sUserAccount
FROM    T_Operations WITH (READUNCOMMITTED)
GROUP BY sUserAccount
HAVING  MAX(dOperationValueDate) < DATEADD(month,-6,DATEADD(month,DATEDIFF(month,0,GETUTCDATE()),0))

作为旁注.. DATEADD(month,-6,DATEADD(month,DATEDIFF(month,0,GETUTCDATE()),0))将返回2016-04-01

如果您想要当前日期,减去六个月,您可以使用DATEADD(month,-6,CAST(GETUTCDATE() AS DATE))DATEADD(month,-6,DATEADD(day,DATEDIFF(day,0,GETUTCDATE()),0)

答案 1 :(得分:0)

.fastq

在dOperationValueDate上有索引