【问题标题】:Store results of SQL Server query for pagination存储 SQL Server 查询的结果以进行分页
【发布时间】:2013-12-05 09:04:37
【问题描述】:

在我的数据库中,我有一个包含相当大数据集的表,用户可以对其执行搜索。因此对于包含大约 250,000 条记录的 Person 表的以下表结构:

firstName|lastName|age
---------|--------|---
John     | Doe    |25
---------|--------|---
John     | Sams   |15
---------|--------|---

用户将能够执行可以返回大约 500 个左右结果的查询。我想做的是允许用户使用分页一次看到他的搜索结果 50。我已经弄清楚了客户端分页的东西,但我需要在某个地方存储查询结果,以便分页使用来自他的唯一查询而不是来自 SELECT * 语句的结果。

任何人都可以就实现这一目标的最佳方式提供一些指导吗?谢谢。

旁注:我一直在尝试通过使用 SELECT INTO 语句来使用临时表来执行此操作,但我认为如果用户 A 执行搜索并且他的结果存储在临时表,然后用户 B 不久后执行搜索,用户 A 的搜索结果被覆盖。

【问题讨论】:

  • 搜索之间有什么不同?
  • 实际查询本身。用户 A 可能搜索 SELECT * FROM Person WHERE age BETWEEN 10 AND 20,用户 B 可能搜索 SELECT * FROM Person WHERE age = 25。对于每个查询,我希望其结果存储在某种类型的缓存中,并且客户端的分页脚本对存储在相应缓存中的结果进行分页,而不是再次运行 SQL 查询。想想 Google 在您进行搜索时提供的体验。

标签: sql sql-server database pagination


【解决方案1】:

从历史上看,对我们来说,管理此问题的最佳方法是创建一个具有唯一名称的完整新表。然后,完成后,您可以安排删除表。

如果可行,该表只包含一个索引 ID(一个简单的序列:1、2、3、4、5)和作为查询一部分的表的主键。不是整个结果集。

然后您的分页逻辑会执行以下操作:

SELECT p.* FROM temp_1234 t, primary_table p 
WHERE t.pkey = p.primary_key 
  AND t.serial_id between 51 and 100

序列号是你的分页索引。

所以,你最终会得到类似的东西(注意,我不是 SQL Server 人,所以请原谅):

CREATE TABLE temp_1234 (
    serial_id serial,
    pkey number
);

INSERT INTO temp_1234
  SELECT 0, primary_key FROM primary_table WHERE <criteria> ORDER BY <sort>;

CREATE INDEX i_temp_1234 ON temp_1234(serial_id); // I think sql already does this for you

如果你可以延迟索引,它会比先创建它更快,但它最有可能是边际改进。

另外,创建一个跟踪表,您可以在其中插入表名和日期。您可以稍后(深夜)将其与收割机进程一起使用,以删除日期表(例如,超过 X 小时的表)。

全表操作比在单个表中插入和删除行要便宜得多:

INSERT INTO page_table SELECT 'temp_1234', <sequence>, primary_key...

DELETE FROM page_table WHERE page_id = 'temp_1234';

这太糟糕了。

【讨论】:

    【解决方案2】:

    在 SQL Server 中,ROW_NUMBER() 函数非常适合分页,并且可能会有所帮助,具体取决于搜索之间的参数变化,例如,如果搜索只是针对不同的 firstName 值,您可以使用:

    ;WITH search AS (SELECT *,ROW_NUMBER() OVER (PARTITION BY firstName ORDER BY lastName) AS RN_firstName
                     FROM YourTable)
    SELECT *
    FROM search 
    WHERE RN BETWEEN 51 AND 100
      AND firstName = 'John'
    

    您可以添加额外的ROW_NUMBER() 行,根据正在搜索的字段更改PARTITION BY 子句。

    【讨论】:

    • 我的一位同事指出,我的“理想”方法会变得过于占用内存,并且使用默认 SQL 分页是可行的方法。这似乎是一个足够干净的解决方案。非常感谢。
    【解决方案3】:

    首先,确保您确实需要这样做。你正在增加显着的复杂性,所以去衡量查询和分页是否真的很痛苦,或者你只是“感觉应该”。使用 ROW_NUMBER() 可以很容易地处理分页。

    假设你继续,一旦你得到你的查询,显然你需要建立一个缓存,所以首先你需要确定键是什么。它将是 SQL 语句或操作标识符(可能是存储过程的名称)和使用的标准。如果您不想在用户之间共享,那么也可以使用用户名或某种会话 ID。

    现在,当您进行查询时,您首先在此表中查找所有关键数据,然后

    a) 找不到它,因此您运行查询并添加到缓存中,根据您想要快照还是实时存储条件/键和数据或数据的 PK。请记住,“实时”并不是真的因为其他用户可能会更改您手下的数据。

    b) 找到它,然后删除结果(或将 PK 连接到基础表)并返回结果。

    当然,现在您需要一个后台进程来清理缓存时间过长的缓存。

    就像我说的 - 在开始之前,您真的应该确保需要这样做。在您给出的示例中,我认为这不值得。

    【讨论】:

    • 非常感谢您的洞察力。我真的没想到会这么复杂。
    猜你喜欢
    • 1970-01-01
    • 2019-12-23
    • 2019-02-10
    • 1970-01-01
    • 2016-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多