在我们的应用程序中,我们有一个网格,用户可以在其中翻阅大量记录(10-2000 万)。网格支持在多列 (20+) 中按升序和降序排序。许多值也不是唯一的,因此应用程序还按 id 排序作为决胜局,以确保行始终出现在同一页面上。例如,如果用户想要按小部件大小(从最大的开始)排序,应用程序会生成一个看起来像这样的查询:
SELECT TOP 30
* -- (Pretend that there is a list of columns here)
FROM Test
-- WHERE widgetSize > 100
ORDER BY
widgetSize DESC,
id ASC
Run Code Online (Sandbox Code Playgroud)
此查询需要大约 15 秒才能运行(使用缓存数据),主要成本似乎是按小部件大小对大约 130 万行进行排序。在尝试调整此查询时,我发现如果我添加一个WHERE
仅限于最大 widgetSizes的子句(在上面的查询中注释掉),则查询只需要约 800 毫秒(所有前 50,000 个结果的小部件大小都大于 100) .
为什么没有WHERE
子句的查询速度会如此之慢?我检查了 widgetSize 列的统计数据,它们显示前 739 行的 WidgetSize > 506。由于只需要 30 行,SQL Server 可以不使用此信息来推断它只需要对具有小部件大小的行进行排序哪个大?
我知道我可以通过在和上添加索引来使这个特定查询更快地执行,但是这个索引只在这个特定场景中有用,并且如果(例如)用户反转排序方向就变得毫无价值。该表包含许多附加列,并且每个索引都很大(~200mb),因此我无法为每个可能的排序顺序添加索引。widgetSize
id
有什么方法可以让这些查询查询执行而不为每个可能的排序顺序添加索引?(用户可以按 20 多列中的任何一列进行排序)
以下脚本创建上表并用一些代表性数据填充它。该表比实际表窄得多,但仍然展示了我所看到的性能。在我的 PC 上,带有 where 子句的查询需要约 200 毫秒,而没有 where caluse 的查询需要约 800 …
我有一个非常大的数据库,大约 100 GB。我正在执行查询:
select * from <table_name>;
Run Code Online (Sandbox Code Playgroud)
我只想显示第 100 到 200 行。
我想了解这是如何在内部发生的。数据库是否将所有记录从磁盘提取到内存中并将第 100 行到第 400 行发送回查询客户端?或者是否存在任何机制,以便仅从数据库中获取那些记录(第 100 个 -200 个) - 通过使用 B 树等索引机制?
我发现这与分页概念有关,但我无法确切地找到它在数据库级别内部是如何发生的。
我正在编写一个查询,用于对社交提要的结果进行分页。这个概念是移动应用程序将请求 N 个项目,并提供我在@CutoffTime
下面调用的开始日期时间。截止时间的目的是确定分页窗口应该何时开始。我们使用时间戳而不是行偏移量的原因是,即使添加了较新的社交内容,时间戳也会让我们在获取较旧帖子时从一致的位置翻页。
由于社交提要项目可以来自您自己或您的朋友,因此我使用 aUNION
来组合这两个组的结果。最初我尝试了TheQuery_CTE
没有的逻辑UNION
,结果很慢。
这就是我所做的(包括相关的表架构):
CREATE TABLE [Content].[Photo]
(
[PhotoId] INT NOT NULL PRIMARY KEY IDENTITY (1, 1),
[Key] UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID(),
[FullResolutionUrl] NVARCHAR(255) NOT NULL,
[Description] NVARCHAR(255) NULL,
[Created] DATETIME2(2) NOT NULL DEFAULT SYSUTCDATETIME(),
);
CREATE TABLE [Content].[UserPhotoAssociation]
(
[PhotoId] INT NOT NULL,
[UserId] INT NOT NULL,
[ShowInSocialFeed] BIT NOT NULL DEFAULT 0,
CONSTRAINT [PK_UserPhotos] PRIMARY KEY ([PhotoId], [UserId]),
CONSTRAINT [FK_UserPhotos_User] FOREIGN KEY ([UserId])
REFERENCES [User].[User]([UserId]),
CONSTRAINT [FK_UserPhotos_Photo] …
Run Code Online (Sandbox Code Playgroud) 使用PostgreSQL 10.5。我正在尝试创建一个分页系统,用户可以在其中来回切换各种结果。
为了不使用OFFSET
,我id
在名为p
(prevId)的参数中从上一页的最后一行传递了。然后我选择id
高于p
参数中传递的数字的前三行。(如本文所述)
例如,如果id
上一页的最后一行是 5,我会选择前 3 行的 anid
大于 5:
SELECT
id,
firstname,
lastname
FROM
people
WHERE
firstname = 'John'
AND id > 5
ORDER BY
ID ASC
LIMIT
3;
Run Code Online (Sandbox Code Playgroud)
这很好用,而且时机也不错:
Limit (cost=0.00..3.37 rows=3 width=17) (actual time=0.046..0.117 rows=3 loops=1)
-> Seq Scan on people (cost=0.00..4494.15 rows=4000 width=17) (actual time=0.044..0.114 rows=3 loops=1)
Filter: ((id > 5) AND (firstname = 'John'::text))
Rows Removed by Filter: …
Run Code Online (Sandbox Code Playgroud) postgresql performance index paging postgresql-10 query-performance
我有这个查询来对结果进行分页,它在 SQL Server 2012 上运行良好。但是我不得不将我的数据库移动到 SQL Server 2008,现在我的存储过程不起作用。我做了一些研究,发现OFFSET
它在 SQL Server 2008 中不起作用。我现在应该使用什么替代方案?我如何使用 SQL Server 2008 实现相同的功能?
这是我的存储过程:
CREATE PROCEDURE [dbo].[sp_JobSearch]
@EnteredKeyword nvarchar(200) = '',
@EnteredLocation nvarchar(200) = '',
@PageNumber INT = 1,
@PageSize INT = 40
AS
BEGIN
SELECT
MasterJob.Title, MasterJob.CompanyName,
MasterJob.ShortDesc, MasterJob.Url,MasterJob.PostedTime,
MasterJob.Location, JobBoard.JobBoardName
FROM
MasterJob
LEFT JOIN
JobBoard ON MasterJob.JobBoardId = JobBoard.JobBoardId
WHERE
(MasterJob.Title LIKE '%' + @EnteredKeyword + '%')
AND (MasterJob.Location LIKE '%' + @EnteredLocation + '%')
ORDER BY
[MasterJobId]
OFFSET @PageSize * (@PageNumber - …
Run Code Online (Sandbox Code Playgroud) 我有一个包含三列的表:HashUID1、HashUID2、Address_Name(这是一个文本电子邮件地址,前两个哈希列是将事件参与者表链接到电子邮件地址的疯狂创建。它丑陋,几乎无法解决由我控制。专注于 address_name 索引)
它有 7800 万行。没有正确排序。无论如何,该索引被拆分到许多快速 LUN 上并执行非常快速的索引查找。
我需要创建一系列查询以一次仅提取 20,000 个“每页行”,但要避免冲突或欺骗。由于没有标识列或易于排序的列,是否有一种简单的方法可以全选并翻页?
我说的是否正确,如果我将 select * from hugetablewithemails 放入临时表,然后通过 row_number 通过它选择该表在事务期间保留在内存中,对我来说,这是过多的内存资源? 这似乎是首选的分页方法。我宁愿按统计百分比分页。:(
有一个索引按顺序维护 address_name 电子邮件地址,并且维护得很好。在过去的一周里,我一直想通过花费一些时间来研究构建一个基于统计的窗口函数吐出范围的过程(我不擅长,但这个查询真的让我感兴趣)来帮助其他开发人员提供索引的一系列字符 1 到(变量)LEFT LIKE 字符,满足 20,000 行——但我什至没有时间开始查询......
几个问题:
有什么建议?不是在寻找实际的代码,只是一些基于经验的提示或建议,也许是警告。我想避免在初始扫描后进行额外的索引扫描。
这是正确的方法吗?
我正在考虑打破所有电子邮件地址的索引总和,收集行数(*),/20,000,并将其用作窗口函数,根据总行数的百分比对最小/最大子字符串(1,5)值进行分组建立分组范围。想法?
这是针对无法修改源数据库的 ETL 过程。
我希望通过一次完整的索引扫描,我可以做到:
查询以根据索引使用情况(按字母顺序排序)获取直方图,并使用 min/max 将其分解(窗口化)以创建这样的一些范围,以便轻松查找所需的索引:
A-> AAAX,(例如 20k 行)AAA-Z,B->(另外 20k),B->BAAR -> BAAR-> CDEFG -> CDEFH > FAAH,等等。
我们为这个 ETL 过程在这些数据库中运行读提交。我们只尝试将它批量处理成 20k 行,因为 DBA 说我们通过抓取完整的表格使用了太多的网络资源。如果数据发生了变化(这是一个问题),我们会即时更新我们的 DW 和临时表。
我喜欢用临时表,但如果我这样做,我会溢出到tempdb中,并获得关于其从DBA的圈套通过电子邮件和数据库太大。
对于大型数据集,使用 an 进行分页OFFSET
是众所周知的,并且不是最好的分页方式。更好的分页方式是使用游标,它只是行上的一个唯一标识符,因此我们知道从最后一个光标位置上次离开的位置继续分页的位置。
当涉及到一个自动递增id
值的游标时,实现起来相当容易:
SELECT * FROM users
WHERE id <= %cursor // cursor is the auto incrementing id, ex. 100000
ORDER BY id DESC
LIMIT %limit
Run Code Online (Sandbox Code Playgroud)
我们不确定的是,如果不是自动递增id
游标,游标的唯一唯一顺序标识符是表行上的uuid
和created_at
。
我们当然可以根据 查询uuid
得到created_at
,然后选择所有的users
,<= created_at
但问题是如果表中有多个相同created_at
时间戳的实例users
怎么办?知道如何users
根据uuid/created_at
游标组合查询表以确保我们获得正确的数据集(就像我们使用自动递增一样id
)?再次,只有独特的领域是uuid
因为created_at
可能是重复的,但他们的组合是每行唯一的。
简介: 我有一个简单的数据库模式,但即使只有几十条记录,基本查询的性能也已经成为一个问题。
数据库:PostgreSQL 9.6
简化架构:
CREATE TABLE article (
id bigint PRIMARY KEY,
title text NOT NULL,
score int NOT NULL
);
CREATE TABLE tag (
id bigint PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE article_tag (
article_id bigint NOT NULL REFERENCES article (id),
tag_id bigint NOT NULL REFERENCES tag (id),
PRIMARY KEY (article_id, tag_id)
);
CREATE INDEX ON article (score);
Run Code Online (Sandbox Code Playgroud)
生产数据信息:
所有表都是读/写的。写入量低,每几分钟左右只有一个新记录。
大概记录数:
每篇文章平均 5 个标签。
问题 …
在来这里打扰你之前,我试图尽可能多地记录我自己的话题,但无论如何我都在这里。
我们想在这个表上实现键集分页:
create table api.subscription (
subscription_id uuid primary key,
token_id uuid not null,
product_id uuid not null references api.product(product_id) deferrable,
spid bigint null,
attributes_snapshot jsonb not null,
created_at timestamp not null,
refreshed_at timestamp,
enriched_at timestamp null,
valid_until timestamp not null,
is_cancelled boolean not null,
has_been_expired boolean not null,
has_quality_data boolean not null
);
Run Code Online (Sandbox Code Playgroud)
为此,我们使用此查询来准备分页元数据:
with book as (
select created_at, subscription_id
from api.subscription
where token_id = $1
and refreshed_at >= $2
and valid_until >= now()
and not is_cancelled
and …
Run Code Online (Sandbox Code Playgroud) 我们有一个包含约 50 亿行的 PostgreSQL 表,它养成了一个讨厌的习惯,即缺少正确的索引并对某些LIMIT
操作进行主键扫描。
问题通常出现在一个ORDER BY .. LIMIT ..
子句(Django 分页中的常见模式)上,其中LIMIT
是索引匹配的结果的一些相对较小的子集。一个极端的例子是这样的:
SELECT * FROM mcqueen_base_imagemeta2
WHERE image_id IN ( 123, ... )
ORDER BY id DESC
LIMIT 1;
Run Code Online (Sandbox Code Playgroud)
其中该IN
子句中的项目约为 20,索引匹配的总行数image_id
为 16。
在EXPLAIN
表明,它错过了image_id
指数,而是确实5B行的PK扫描:
限制(成本=0.58..4632.03 行=1 宽度=28) -> 在 mcqueen_base_imagemeta2 上使用 mcqueen_base_imagemeta2_pkey 向后扫描索引(成本=0.58..364597074.75 行=78722 宽度=28) 过滤器:(image_id = ANY ('{123, ...}'::bigint[]))
如果LIMIT
增加到2
,它会按预期工作:
限制(成本=7585.92..7585.93 行=2 宽度=28) -> 排序(成本=7585.92..7782.73 行=78722 宽度=28) 排序键:id DESC -> 在 mcqueen_base_imagemeta2 上使用 …
postgresql performance index-tuning paging postgresql-9.6 query-performance
paging ×10
postgresql ×4
sql-server ×4
performance ×3
t-sql ×2
cte ×1
cursors ×1
index ×1
index-tuning ×1
join ×1
mysql ×1
mysql-5.7 ×1
select ×1
sorting ×1