sql - 2 列的范围查询

标签 sql sql-server database query-optimization

我有一个非常大的表 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 上创建索引

有人可以建议我一些实现此目标的方法吗?

最佳答案

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

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 表。

关于sql - 2 列的范围查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27580384/

相关文章:

database - 我如何在 Redis 中组织汽车的制造商、型号和生成

使用 IN 子句时 SQL Offset 总行数变慢

mysql - MySQL 中的更改数据捕获

sql-server - 全文搜索适合搜索人名吗?

database - DAAB,使用数据库实例的最佳方法是

database - 索引两个 varchar 列以使用字符串连接进行查询

c# - ADO.NET:将数据从一个表获取到另一个?

php - 当我在 mysql 表结构中使用大量索引时,查询性能会降低

.net - 在 SQL Server 中,SESSIONPROPERTY 和 @@Options 之间有什么区别?

sql - T-SQL 按独特的多对多关系性能分组