【问题标题】:Sql query optimizationsql查询优化
【发布时间】:2013-01-10 16:50:35
【问题描述】:

我正在尝试优化 SQL 查询。问题是它非常慢!我正在使用 sqlite 和中型数据集(20000row)。我的 sql 语法如下所示:

SELECT DISTINCT date(A.last_update) as lastUpdate,
(SELECT COUNT(*) FROM bgp_update AS B WHERE date(B.last_update) = date(A.last_update) AND B.validity = 1) AS valid,
(SELECT COUNT(*) FROM bgp_update AS B WHERE date(B.last_update) = date(A.last_update) AND B.validity = 0) AS invalid,
(SELECT COUNT(*) FROM bgp_update AS B WHERE date(B.last_update) = date(A.last_update) AND B.validity = -1) AS notFound
FROM (SELECT DISTINCT last_update FROM bgp_update ORDER BY last_update) AS A WHERE last_update BETWEEN '%var' and '%var2';

以下是我拥有的示例:

id   |     last_update   |  Validity

48   | 2009-1-6 18:34:38 |  notFound   
47   | 2009-1-6 18:34:38 |  valid   
46   | 2009-1-6 18:34:38 |  valid    
45   | 2009-1-3 18:34:38 |  invalid   
44   | 2009-1-3 18:34:38 |  invalid   
42   | 2009-1-4 18:34:38 |  notFound 
41   | 2009-1-4 18:34:38 |  notFound
48   | 2009-1-4 18:34:38 |  valid

查询结果如下:

    Date   |  valid   |  invalid   |    notFound

  2009-1-3 |   0      |     2      |       0
  2009-1-4 |   1      |     0      |       2
  2009-1-6 |   2      |     0      |       1

我需要这个来生成一个折线图。例如:line chart!

但正如我提到的,查询非常慢!我怎样才能让它更快?

:::: 2. 编辑 :::::

前面的描述我已经简化了问题。我的表创建语句如下所示:

CREATE TABLE bgp_update
    (id INTEGER PRIMARY KEY,
    ip VARCHAR(64) NOT NULL,
    mask INTEGER NOT NULL,
    asn INTEGER NOT NULL,
    validity INTEGER NOT NULL,
    last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP)

如您所见,我的查询仅使用 last_update 和有效性字段。

【问题讨论】:

  • 好吧,你没有具体说明问题是什么,预期的结果是什么,表架构,索引等。我们不是读心术的人。
  • 对不起,我会编辑问题。
  • 查询优化器告诉你什么?
  • 您需要向我们展示表和索引定义。 诊断慢查询需要完整的表和索引定义,而不仅仅是描述或解释。也许您的表格定义不佳。也许索引没有正确创建。也许您认为您在该列上没有索引。没有看到表和索引定义,我们无法判断。如果您知道如何填写EXPLAIN,请将结果也放入问题中。
  • date('2009-1-6 18:34:38') 将是 NULL,因为月份和日期不是零填充的。你的示例数据真的正确吗?

标签: sqlite query-optimization


【解决方案1】:

嵌套子查询有时会使查询效率很低。您可以考虑重写查询,以便它使用连接。

其次,您可以重新排列连接元素,其中记录最少的表最接近查询的开头。这样做会减少 MySQL 为从其他表中检索行而必须排序的记录数。

此外,您可能需要考虑在 ID 列和具有您正在查看的特定值的表的列上使用索引。

如果一切都失败了,您可能会发现三个单独的查询可能是最有效的。

【讨论】:

    【解决方案2】:

    您可以在 case 语句上使用 sum,例如:

    select last_update,
      sum(case when validity=1 then 1 else 0 end) valid,
      sum(case when validity=0 then 1 else 0 end) invalid,
      sum(case when validity=-1 then 1 else 0 end) notfound
    from bgp_update
    where last_update between '1/1/2013' and '1/2/2013'
    group by last_update
    order by last_update
    

    SQL Fiddle

    【讨论】:

    • 这样每天会有多条记录,validity列不包含0/1/-1值,日期格式错误。
    【解决方案3】:

    假设日期值在 proper format 中以被 date 函数理解,您可以简单地将要检查的条件相加(因为布尔表达式只返回 0 或 1):

    SELECT date(last_update) AS Date,
           sum(validity = 'valid'   ) AS valid,
           sum(validity = 'invalid' ) AS invalid,
           sum(validity = 'notFound') AS notFound
    FROM bgp_update
    WHERE last_update BETWEEN ? AND ?
    GROUP BY date(last_update)
    ORDER BY date(last_update)
    

    如果您在 last_update 列上有索引,则此查询会很有效,如果您在 last_update 和 validity 列上有 covering index,则效率会更高。

    【讨论】:

    • +1 for sum(validity=..) 代替 sum(case when ... end) 并且看起来更干净
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-04
    • 2014-11-16
    • 2011-06-22
    • 2015-08-10
    相关资源
    最近更新 更多