2013-06-26 90 views
2

我有一个表(可以称之为audit),看起来像这样:选择从范围内的最低日期和排除另一个范围

+--------------------------------------------------------------------------+ 
| id | recordId | status | mdate     | type | relatedId  | 
+--------------------------------------------------------------------------+ 
| 1 | 3006  | A  | 2013-04-03 23:59:01.275 | type1 | 1    | 
| 2 | 3025  | B  | 2013-04-04 00:00:02.134 | type1 | 1    | 
| 3 | 4578  | A  | 2013-04-04 00:04:30.033 | type2 | 1    | 
| 4 | 7940  | C  | 2013-04-04 00:04:32.683 | type1 | <NULL>  | 
| 5 | 3006  | D  | 2013-04-04 00:04:32.683 | type1 | <NULL>  | 
| 6 | 4822  | E  | 2013-04-04 00:04:32.683 | type2 | <NULL>  | 
| 7 | 3006  | A  | 2013-04-04 00:06:54.033 | type1 | 2    | 
| 8 | 3025  | C  | 2013-04-04 00:06:54.033 | type1 | 2    | 

...和对数百万行的。而另一张表,我们将拨打related

+-------------+ 
| id | source | 
+-------------+ 
| 1 | src_X | 
| 2 | src_Y | 
| 3 | src_Z | 
| 4 | src_X | 
| 5 | src_X | 

......并开启数十万行。

这两个表格上的列数多于这些列,但这是我们需要描述问题的全部内容。列relatedId加入related表。 recordId也加入到另一个表中,并且audit中将有多个条目与recordId相同。

我试图创建将产生以下输出的查询:

+-----------------+ 
| source | count | 
+-----------------+ 
| src_X | 1643 | 
| src_Y | 255 | 
| NULL | 729 | 
+-----------------+ 

的计数的记录中audit数量已经给定type(如"type1"),是一个集内的状态(例如,"A", "B", "C"),然后将其外部加入related并按source分组。

美中不足的是,我只希望包括audit是在特定日期范围内的内记录,而我也只是想从audit加入到related在该范围内,最早的条目为每个recordId。此外,我想忽略任何与typestatus条件匹配的记录,但是具有相同的recordId的条目比日期范围更早。

所以,从上面的例子中的数据阐明:可以说,我想的类型的type1"A", "B", "C"2013-04-042013-04-05日期范围的状态值。第2行和第4行将包含在计数中。第3行被排除,因为它有不正确的type。由于状态不正确,第5行被排除。第6行被排除,因为状态和类型都不正确。排除第1行,因为它在日期范围之外。第7行也被排除,因为还有一行(第1行)与状态和类型标准相匹配,并且具有相同的recordId,该行比日期范围的开始时间更早。第8行被排除,因为第8行和第2行具有相同的recordId并符合标准,但我们只计算范围内两个最旧的记录。

换句话说,我想只计算给定recordId的条目第一次出现在表中并且在目标日期范围内。

我们已经想出了以下内容:

WITH data (recordId, id) AS (
    SELECT a.recordId, MIN(a.id) 
    FROM audit a 
    WHERE a.status in ('A','B','C') 
     AND type = 'type1' 
    GROUP BY a.recordId 
) 
SELECT r.source, COUNT(*) 
FROM data d 
    JOIN audit a ON d.id = a.id 
    LEFT JOIN related r ON a.relatedId = r.id 
WHERE a.mdate >= '2013-04-04 00:00:00.000' 
    and a.mdate < '2013-04-05 00:00:00.000' 
GROUP BY r.source 

这将MSSQL Server 2008上运行,目前依赖于一个事实,即审计表ID是自动生成的。由于id是在插入记录时生成的,并且mdate也是插入时间戳,并且记录一旦插入就不会更新,所以我认为这是正确的。该查询似乎给出了有限的一组测试数据的正确输出,但我希望得到第二个意见。

  • 此查询是否正常?
  • 其性能可以提高吗?
+1

计算表格表达式中的日期范围可能会提高性能。 –

+0

好点。将'AND a.mdate <'2013-04-05 00:00:00.000''添加到计算表中将有助于限制它返回的记录数。 –

+0

为了提高查询性能,请考虑索引。在WHERE Clause Fields,Join Fields上使用索引,然后再次测试性能。 – 2013-06-26 11:58:25

回答

4

您可以使用ROW_NUMBER()函数根据RecordId和mDate对记录进行排名,然后将结果限制为第一次出现在指定日期之间的位置。

WITH data AS 
( SELECT a.relatedId, a.mdate, rn = ROW_NUMBER() OVER(PARTITION BY a.RecordId ORDER BY a.mdate) 
    FROM audit a 
    WHERE a.status in ('A','B','C') 
    AND  type = 'type1' 
) 
SELECT r.source, [Count] = COUNT(*) 
FROM data d 
     LEFT JOIN related r 
      ON d.relatedId = r.id 
WHERE d.rn = 1 
AND  d.mdate >= '2013-04-04 00:00:00.000' 
AND  d.mdate < '2013-04-05 00:00:00.000' 
GROUP BY r.source; 

我不确定这会比目前的解决方案执行得更好,但会解决依赖按时间顺序插入的问题。如果按时间顺序插入不成问题,则可以将ROW_NUMBER()函数中的ORDER BY更改为使用ID,因为对集群密钥的排序会更快。

从外部查看性能调整非常困难,为了甚至猜测它,我们需要查看相关表上的索引以及查询的执行计划。然后,您可以识别瓶颈,以及哪些索引可以提高性能。

This SQL Fiddle显示了两个查询(我和你)有相同的结果结束了,但是当你看看IO统计数据,你可以看到你查询你:

(2 row(s) affected) 
Table 'Related'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Audit'. Scan count 2, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

使用ROW_NUMBER()你得到:

(2 row(s) affected) 
Table 'Related'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Audit'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

关键因素是一个较少的逻辑阅读。快速查看执行计划显示,ROW_NUMBER()解决方案的分支少一个,估计为批处理成本的37%,而您的解决方案为63%,因此对于这一小部分数据,它似乎是一个性能改进。

enter image description here

然而也只有这么多,我可以从数据的这样一个小样本出​​来,一些解决方案不能很好地扩展,正如我已经说过,将取决于你的数据的大小和分布状态。我的建议是尝试不同的解决方案,通过检查IO统计数据和执行计划来找到瓶颈。

例如,在看的CTE这占了我的查询查询成本的50%执行计划:

enter image description here

通过添加该指数:

CREATE INDEX IX_Audit_ALL ON Audit (recordId, MDate, RelatedID, status, type) 

我能够将其降低到查询成本的18%。

enter image description here

然而,在不知道我越不能明确说这个实用性将指标(一)帮助这个查询与您的数据和(b),它不会导致其他问题与您的数据库通过减慢插入/更新。

+0

感谢您的全面回答! –