【问题标题】:How to catch distinct data in a specific column of a SQLite database very fast?如何快速捕获 SQLite 数据库特定列中的不同数据?
【发布时间】:2015-07-02 23:54:46
【问题描述】:

我有以下情况:有一个表,其中一列是日期时间字符串。我想做的是知道这张表中提到了哪些年份,即:我想阅读“日期时间”列中的所有行,看看它们中的哪些年份与我的“已经提到的年份列表”不同;如果给定行还没有添加年份,我会将那一年添加到我的列表中,最后我会提到所有年份。

现在我很自然地知道如何在一个非常昂贵的算法中做到这一点,这将是上面文本的文字实现。我想知道的是,是否有一种快速的方式,也许是更“原生”的方式来进行这种查询 - 你可以想象如果我尝试完全按照上面所说的那样实现算法,我会得到巨大的处理器成本.

有更好的想法吗?

详细信息:我正在使用 C++/Qt 编程,表可能很大(例如 10000 行或更多)。

【问题讨论】:

    标签: sql algorithm performance sqlite


    【解决方案1】:

    您可以选择 min(date-time) 和 max(date-time) 并从中获取年份,然后遍历其间的所有年份,检查在有问题的年份。不确定它会更快,但可能值得一试。

    或者您可以选择不同的 X,其中 X 是返回日期的年份部分的函数。这可能是最原生、最干净的方式。恐怕日期操作函数往往是特定于 sql 平台的,所以我不确定 sqlite 的语法。

    好像是这样

    select distinct strftime('%Y', date-time) as Year
    

    【讨论】:

      【解决方案2】:

      为了获得最佳性能,datetime 字符串列上需要有一个合适的索引。我将建议(不一定推荐)一种方法,以避免某些其他查询模式的潜在性能问题。

      我建议的方法是使用多个查询,每个查询返回包含新年份值的单行。 (我假设只有几个不同的年份值,并且给定年份有很多行。)

      假设我现有的年份列表包含 2011、2013 和 2014 年。

      下面描述了我将运行的查询序列,在我运行的查询中使用现有值作为谓词。基本思想是我只需要在给定的年份中找到一行...无需读取 所有 行。

      我需要现有的年份列表才能排序。我会从最低值开始,然后运行一个查询来获取该年之前的最早日期。我想要一个最有效地利用索引的查询,以及 Sqllite 中可用的优化。

      我现有列表中最早的年份值是“2011”。我会把它推到查询中......我的第一个镜头是这样的:

       select dt from t where dt < '2011-01-01'
        order by dt limit 1
      

      如果我不追回,我知道 2011 年是最早的一年。

      如果我确实得到了回报,我知道这是一个“新”的一年。我会将前四个字符作为年份,并将其添加到我的列表中。我会将那个年份的值与 2011 年进行比较,如果差值大于 1,我会检查下一个最低年份。

      例如,如果该查询返回以“2008”开头的日期,则我运行的下一个查询将检查日期时间最短的行,该行在 2008 年之后但在 2011 年之前的一年。

       select dt from t where dt < '2011-01-01'
          and dt >= datetime('2008-01-01','+1 years') 
        order by dt limit 1
      

      如果我没有返回一行,我知道 2011 年之前没有更多的“新”年份值。我的下一个查询将使用 2011 作为下限,并将现有列表中的下一年值作为上限绑定,然后再次重复相同的查询。

      如果我确实找回了行,日期时间从 2009 年开始。我会将 2009 年添加到我的列表中,我的下一个查询与上面的查询完全相同,但用 2009 年代替 2008 年...

       select dt from t where dt < '2011-01-01'
          and dt >= datetime('2009-01-01','+1 years') 
        order by dt limit 1
      

      再一次,如果我没有吵架,那么我知道 2011 年之前没有新的年份了。

      所以,现在 2011 年是我的下限,而我现有列表中的下一年是上限。因此,再次进行相同的查询,仅更改年份文字...

       select dt from t where dt < '2013-01-01'
          and dt >= datetime('2011-01-01','+1 years') 
        order by dt limit 1
      

      如果我得到一个连续的,那就是新的一年添加到我的列表中。这是我下一个查询的新下限。如果没有行,那么我最后一个查询的上限就是新的下限。

      为了优化模式,我会跳过运行我知道不会返回行的查询。当我的列表中已经有 2013 年和 2014 年时,我的查询将采用这种模式...

       select dt from t where dt < '2014-01-01'
          and dt >= datetime('2013-01-01','+1 years') 
        order by dt limit 1
      

      但我们知道没有满足这两个条件的行。一行的dt值不能小于2014大于等于2014,这是不可能的条件,所以我们可以跳过执行。

      当我到达列表中的最后一个值时,我将删除上限条件...我不在乎下一个查询返回 2015、2017 还是 2032...无论最近一年之后的最低年份我的列表中有年份。

       select dt from t where 
              dt >= datetime('2014-01-01','+1 years') 
        order by dt limit 1
      

      如果我得到了一个回报,将那一年添加到列表中,并将其用作我的下一个下限。并重复,直到我没有得到任何回报。

      这确实会运行多个查询,但它们应该非常有效。在 Hugh Jass 表上,这些可能是您可以运行以查找新年值的最有效查询。

      当我们需要运行大量查询时,当我们需要检查很多“差距”时,这种模式就会崩溃。

      这种模式的最坏情况是数百个现有年份值以奇数结尾。每年的值之间存在差距,我们必须检查它们之间的每个差距。

      但这种模式的最佳情况是一长串连续年份值。如果没有找到新的年份值,我们最多会运行 两个 查询。一个用于检查较早的年份(未找到),另一个用于检查较晚的年份(未找到)。


      同样,这种方法的性能绝对取决于在 dt 上是否有适当的索引,以及有效利用该索引的查询计划。

      【讨论】:

      • 我们不一定需要使用datetime 函数为日期文字添加年份,我们可以在客户端上轻松处理,将年份值加1。 (我使用 datetime 函数来说明我们使用的是我们检索到的年份值,或者在我们的列表中。这也假设“日期时间字符串”列以一致的格式存储,前导日期部分在格式为“yyyy-mm-dd”。
      • 如果我们从一个现有的 empty 年份值列表开始,我们的第一个查询将在 dt 列上没有谓词(条件),只获取最早的。
      • 感谢对这个有趣算法的仔细解释!顺便说一句,您的假设是正确的:尽管数据库可能会使用 30 年,但在大多数情况下,我会有一个大的同一年连续条目列表(格式相同,yyyy/MM/dd。跨度>
      • @Momergil:yyyy/mm/dd 格式不是 Sqllite 用于日期或时间字符串的格式之一。我假设您使用的是受支持的格式 yyyy-mm-dd。显然,您可以存储您想要的任何字符串,但上面的查询旨在使用 Sqllite 日期时间字符串。 https://www.sqlite.org/lang_datefunc.html 需要调整查询以删除对 Sqllite 日期时间函数的引用。
      • 从一个空数组开始,如果表中有 30 个不同的年份值,该算法将执行 31 个查询以返回这些值。 (此算法旨在作为一种快速检查是否添加了任何新“年份”值的方法,并期望在大多数情况下运行它,它不会找到任何值。设计的目的是避免重新检查我们已经知道的年份值。
      【解决方案3】:

      我不熟悉 SqlLite 的细节,但它在某些方面接近 ANSI:一种区分计算值的快速方法,就是说

      select strftime('%Y',datetime) 
      from TABLE 
      group by strftime('%Y',datetime)
      

      在 sqlserver 中不使用聚合函数的 group by 将返回不同的单列。

      如果 sqlite 不那么有利,您还可以选择另一列的计数,以将年份限制为不同

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-03
        • 2016-05-08
        • 1970-01-01
        • 2019-07-27
        • 1970-01-01
        相关资源
        最近更新 更多