将下一个唯一值添加到SQL列

时间:2016-01-05 21:54:27

标签: performance sqlite join optimization

我有两个表,我试图根据两个标准加入。其中一个标准是来自t1的日期介于t2中的日期和t2中的下一个日期之间。另一个是来自t1的名称与来自t2的名称匹配。

即。如果t2看起来像这样:

Record     Name     Date
1          A1234    2016-01-03 04:58:00 
2          A1234    2015-12-15 08:34:00
3          A5678    2016-01-04 03:14:00
4          A1234    2016-01-05 21:06:00

然后:

  • 来自t1的名称为A1234,日期介于2016-01-03 04:58:002016-01-05 21:06:00之间的任何记录都会加入到记录1中。
  • 来自t1的名称为A1234且日期介于2015-12-15 08:34:002016-01-03 04:58:00之间的任何记录都将加入记录2
  • 记录4日期之后来自t1的A1234的任何记录将加入记录4
  • 来自t1的A5678的任何记录都将加入记录3,因为只有一个日期。

我最初的方法是使用相关子查询来查找下一个日期。但是,由于记录数量很多,我确定这将花费一年多的时间来执行,因为它会在每次迭代期间搜索t2的所有下一个日期。原SQLite:

CREATE TABLE outputtable AS SELECT * FROM t1, t2 d
WHERE t1.Name = d.Name AND t1.Date BETWEEN d.Date AND (
    SELECT * FROM (
        SELECT Date from t2
        WHERE t2.Name = d.Name
        ORDER BY Date ASC )
    WHERE Date > d.Date
LIMIT 1 )

现在,我想为t2中的所有记录找到下一个日期,并在t2中创建一个包含下一个日期的新列。这样,我只搜索下一个约400,000次而不是560亿次,大大提高了我的表现。

因此,我查找的查询的输出将使t2看起来像这样:

Record     Name     Date                    Next_Date
1          A1234    2016-01-03 04:58:00     2016-01-05 21:06:00
2          A1234    2015-12-15 08:34:00     2016-01-03 04:58:00
3          A5678    2016-01-04 03:14:00     2999-12-31 23:59:59
4          A1234    2016-01-05 21:06:00     2999-12-31 23:59:59 

然后,我可以简单地查询t1.Date是否在t2.Datet2.Next_Date之间。

如何构建一个查询,将下一个日期添加到t2中的新列?

3 个答案:

答案 0 :(得分:0)

您应该只使用下面的查询来加入表格,而不是添加新列:

SELECT
    T1.*,
    T2_1.*
FROM
    T1
INNER JOIN T2 T2_1 ON
    T2_1.Name = T1.Name AND
    T2_1.some_date < T1.some_date
LEFT OUTER JOIN T2 T2_2 ON
    T2_2.Name = T1.Name AND
    T2_2.some_date > T2_1.some_date
LEFT OUTER JOIN T2 T2_3 ON
    T2_3.Name = T1.Name AND
    T2_3.some_date > T2_1.some_date AND
    T2_3.some_date < T2_2.some_date
WHERE
    T2_3.Name IS NULL

您可以使用NOT EXISTS执行相同操作,但此方法通常具有更好的性能。

答案 1 :(得分:0)

您可以使用适当的索引加速(子)查询。 要检查实际使用的索引,请使用Odata Controller: How to convert Odata response to C# object at client

您的原始查询,没有任何索引,将由SQLite 3.10.0执行,如下所示:

0|0|0|SCAN TABLE t1
0|1|1|SEARCH TABLE t2 AS d USING AUTOMATIC COVERING INDEX (name=?)
0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 1
1|0|0|SCAN TABLE t2
1|0|0|USE TEMP B-TREE FOR ORDER BY

(“自动”索引仅为此查询临时创建;优化程序估计这仍然比不使用任何索引更快。)

在这种情况下,您可以通过索引用于查找的所有列来获得最佳查询计划:

create index i1nd on t1(name, date);
create index i2nd on t2(name, date);
0|0|1|SCAN TABLE t2 AS d
0|1|0|SEARCH TABLE t1 USING INDEX i1nd (name=? AND date>? AND date<?)
0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 1
1|0|0|SEARCH TABLE t2 USING COVERING INDEX i2nd (name=? AND date>?)

答案 2 :(得分:-1)

我已经在大约1 mil行的表上使用了这种方法并且成功了。显然,创建一个覆盖此查询的索引将有助于提高性能。

此方法使用#[derive(Copy, Clone)] struct AngleUnit; struct Point { x: f32, y: f32, z: f32, unit: AngleUnit, } fn factor(from: AngleUnit, to: AngleUnit) -> f32 { 1f32 } impl Point { pub fn new(x: f32, y: f32, z: f32, unit: AngleUnit) -> Point { Point { x, y, z, unit } } fn scale(&mut self, factor: f32) { self.x *= factor; self.y *= factor; self.z *= factor; } fn convert(&mut self, unit: AngleUnit) { let point_unit = self.unit; self.scale(factor(point_unit, unit)); } } fn main() {} 创建要加入的值。在CTE中创建RANK后(出于可读性原因使用此方法,请更正样式或个人偏好),使用子查询将rnk连接到rnk + 1;又名下一个日期。

以下是使用示例值代码的示例。

RANK
  

编辑:在下面添加了代码输出。

上面的代码产生以下输出。

IF OBJECT_ID('tempdb..#T2') IS NOT NULL
    DROP TABLE #T2

CREATE TABLE #T2
(
    Record INT NOT NULL PRIMARY KEY,
    Name VARCHAR(10),
    [DATE] DATETIME,
)

INSERT INTO #T2
VALUES (1, 'A1234', '2016-01-03 04:58:00'),
    (2, 'A1234', '2015-12-15 08:34:00'),
    (3, 'A5678', '2016-01-04 03:14:00'),
    (4, 'A1234', '2016-01-05 21:06:00');

WITH Rank_Dates
AS (Select *
,rank() OVER(PARTITION BY #t2.name ORDER BY #t2.date DESC) AS rnk
 FROM #T2)
 select RD1.Record,
 RD1.Name,
 RD1.DATE,
 COALESCE (RD2.DATE, '2999-12-31 23:59:59') AS NEXT_DATE 
 FROM Rank_Dates RD1
    LEFT JOIN Rank_Dates RD2
        ON RD1.rnk = RD2.rnk + 1 
            AND RD1.Name = RD2.Name
ORDER BY RD1.Record -- ORDER BY is optional
;

随机说明。使用Record Name DATE NEXT_DATE 1 A1234 2016-01-03 04:58:00.000 2016-01-05 21:06:00.000 2 A1234 2015-12-15 08:34:00.000 2016-01-03 04:58:00.000 3 A5678 2016-01-04 03:14:00.000 2999-12-31 23:59:59.000 4 A1234 2016-01-05 21:06:00.000 2999-12-31 23:59:59.000 代替硬编码CURRENT_TIMESTAMP会产生类似的结果吗?