2列的范围查询

时间:2014-12-20 12:45:59

标签: sql sql-server database query-optimization

我有非常大的桌子Shelve(大约1亿),它有书架的搁架信息。

搁置

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

  6. 有人可以建议我采取一些方法来实现这个目标吗?

1 个答案:

答案 0 :(得分:2)

如果您想为每本书设置一个搁置值,您可以尝试:

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并且图书范围不重叠。

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

编辑:

您可以使用ShelveId声明处理丢失的case

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;

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

编辑II:

如果您需要其他属性,请尝试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表。