【问题标题】:Range queries on 2 columns2列的范围查询
【发布时间】:2014-12-20 12:45:59
【问题描述】:

我有非常大的桌子 Shelve(大约 1 亿个),其中包含书籍的 Shelve 信息。

搁置

ShevleID     RangeStart      RangeEnd  
----------------------------------------
   1               1           100
   2             200           500
   3             501           1000

每本书都有唯一的编号 BookID。假设您有一本书的 BookID 为 50。 那么 Book 必须保存在 Shelve 1 中,因为 50 介于 1 和 100 之间。

书籍

BookID     BookName
---------------------------
   1       Book1
   2       Book2
   .
   .
  50       Book3

我的查询是这样的-

SELECT 
    BookID, 
    BookName, 
    ShelveID
FROM 
    Book B
LEFT JOIN  
    Shelve S 
      ON B.BookID 
                BETWEEN 
                       S.RenageStart 
                       AND
                       S.RangeEND

此查询非常慢,因为查询一次只能使用 RangeStart 或 RangeEnd 列之一的索引。

这5个选项我已经试过了-

  1. 在 StartIP 上创建索引

  2. 在 EndIP 上创建索引

  3. 在 StartIP 上创建包含索引(包含列 EndIP)

  4. 在 EndIP 上创建包含索引(包含列 StartIP)

  5. 在 StartIP、EndIP 上创建索引

有人可以建议我一些方法来实现这一点吗?

【问题讨论】:

  • 您的查询返回一行还是多行?
  • 欢迎使用 StackOverflow:如果您发布代码、XML 或数据示例,在文本编辑器中突出显示这些行并单击“代码编辑器工具栏上的示例”按钮 ({ }) 以很好地格式化和语法突出显示它!
  • 为什么不将分配的 ShelveID 存储在 book 表中?这就是属性所属的位置,因为 Book 和 Shelve 之间的关系为零或一。非规范化设计是性能问题的根本原因,因为从性能角度来看,加入一个不等式运算符是有问题的。
  • @Gordon Linoff 此查询返回超过 1 行。每本书一行。
  • @Dan - 我们必须每 3 天左右更改一次图书的位置,并且 Shelve 表由密码填充。shelve 表定期更改,因此我们无法将 shelveID 存储在 Book 表中。我们可以优化读取部分。

标签: sql sql-server database query-optimization


【解决方案1】:

如果您希望每本书都有一个货架价值,您可以尝试:

SELECT b.*,
       (SELECT TOP 1 s.ShelveId
        FROM Shelve S
        WHERE b.BookId >= s.RangeStart
        ORDER BY s.RangeStart DESC
       ) as ShelveId
FROM Book B;

这应该可以有效地使用Shelve(RangeStart, ShelveId) 上的索引。

这假设您想要一个 ShelveId 并且图书范围不重叠。

我很好奇你真正的应用是什么。没有图书馆(据我所知)拥有数亿本书。

编辑:

您可以使用case 语句处理丢失的ShelveId

SELECT b.*,
       (SELECT TOP 1 (case when b.BookId between s.RangeStart and s.RangeEnd then s.ShelveId end)
        FROM Shelve S
        WHERE b.BookId >= s.RangeStart
        ORDER BY s.RangeStart DESC
       ) as ShelveId
FROM Book B;

如果其他假设成立,这可能会解决您的问题。

编辑二:

如果您想要其他属性,请尝试cross apply。它应该具有类似的性能特征:

SELECT b.*,
       s.*
FROM Book B CROSS APPLY
     (SELECT TOP 1 (case when b.BookId between s.RangeStart and s.RangeEnd then s.ShelveId end) as RangeStart, . . .
        FROM Shelve S
        WHERE b.BookId >= s.RangeStart
        ORDER BY s.RangeStart DESC
       ) s

现在,进行一些实验。我想写:

SELECT b.*,
       s.*
FROM Book B CROSS APPLY
     (SELECT TOP 1 s.*
        FROM Shelve S
        WHERE b.BookId >= s.RangeStart and b.BookId <= s.RangeEnd
        ORDER BY s.RangeStart DESC
       ) s

但是,这可能会混淆优化引擎并阻止索引的使用。如果它有效,那就太好了。如果它不起作用,我建议对每个变量使用带有case 的第一个版本。或者,使用相关子查询版本并在主键上连接回 Shelve 表。

【讨论】:

  • 是的图书范围不重叠。这是在线书店的真实应用。
  • 这里有一个问题。 bookID 可能不存在于任何给定范围之间。在这种情况下,我必须返回 null。在 bookID 150 shevleID 的给定情况下,应该为 NULL。
  • 非常感谢。这种方法显示出良好的性能改进。只是一个小问题。我需要从搁置表中获取除 ShelveID 之外的其他一些属性。将这个结果与 Shelve Table 结合是获得该结果的唯一方法还是有更好的方法?我想要 (Book.* , ShelveID,ShelveName,ShelveServerID........) 列在结果中
  • Case 语句仍然省略了不在任何范围内的 bookID
  • 我的错 - 实际上将“ORDER BY s.RangeStart”更改为“ORDER BY s.RangeStart DESC”有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-20
相关资源
最近更新 更多