RuS*_*SSe 8 performance azure-sql-database query-performance
在我的应用程序中,我必须连接数百万行的表。我有一个这样的查询:
SELECT DISTINCT "f"."id" AS "FileId"
, "f"."name" AS "FileName"
, "f"."year" AS "FileYear"
, "vt"."value" AS "value"
FROM files "f"
JOIN "clients" "cl" ON("f"."cid" = "cl"."id" AND "cl"."id" = 10)
LEFT JOIN "value_text" "vt" ON ("f"."id" = "vt"."id_file" AND "vt"."id_field" = 65739)
GROUP BY "f"."id", "f"."name", "f"."year", "vt"."value"
Run Code Online (Sandbox Code Playgroud)
表“files”有1000万行,表“value_text”有4000万行。
这个查询太慢了,执行需要 40 秒(15000 个结果) - 3 分钟(65000 个结果)。
我曾想过将两个查询分开,但我不能,因为有时我需要按连接的列(值)排序...
我能做什么?我将 SQL Server 与 Azure 结合使用。具体而言,具有定价/模型层“PRS1 PremiumRS (125 DTU)”的 Azure SQL 数据库。
我收到了大量数据,但我认为互联网连接不是瓶颈,因为在其他查询中,我也收到了大量数据,而且速度更快。
我尝试使用客户端表作为子查询并DISTINCT
使用相同的结果进行删除。
我在客户端表中有 1428 行。
clients
桌子:
CREATE TABLE [dbo].[clients](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[code] [nvarchar](70) NOT NULL,
[password] [nchar](40) NOT NULL,
[name] [nvarchar](150) NOT NULL DEFAULT (N''),
[email] [nvarchar](255) NULL DEFAULT (NULL),
[entity] [int] NOT NULL DEFAULT ((0)),
[users] [int] NOT NULL DEFAULT ((0)),
[status] [varchar](8) NOT NULL DEFAULT ('inactive'),
[created] [datetime2](7) NULL DEFAULT (getdate()),
[activated] [datetime2](7) NULL DEFAULT (getdate()),
[client_type] [varchar](10) NOT NULL DEFAULT ('normal'),
[current_size] [bigint] NOT NULL DEFAULT ((0)),
CONSTRAINT [PK_clients_id] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON),
CONSTRAINT [clients$code] UNIQUE NONCLUSTERED
(
[code] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
Run Code Online (Sandbox Code Playgroud)
files
桌子:
CREATE TABLE [dbo].[files](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[cid] [bigint] NOT NULL DEFAULT ((0)),
[eid] [bigint] NOT NULL DEFAULT ((0)),
[year] [bigint] NOT NULL DEFAULT ((0)),
[name] [nvarchar](255) NOT NULL DEFAULT (N''),
[extension] [int] NOT NULL DEFAULT ((0)),
[size] [bigint] NOT NULL DEFAULT ((0)),
[id_doc] [bigint] NOT NULL DEFAULT ((0)),
[created] [datetime2](7) NULL DEFAULT (getdate())
CONSTRAINT [PK_files_id] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON),
CONSTRAINT [files$estructure_unique] UNIQUE NONCLUSTERED
(
[year] ASC,
[name] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
GO
ALTER TABLE [dbo].[files] WITH NOCHECK ADD CONSTRAINT [FK_files_client] FOREIGN KEY([cid])
REFERENCES [dbo].[clients] ([id])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[files] CHECK CONSTRAINT [FK_files_client]
GO
Run Code Online (Sandbox Code Playgroud)
value_text
桌子:
CREATE TABLE [dbo].[value_text](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[id_file] [bigint] NOT NULL DEFAULT ((0)),
[id_field] [bigint] NOT NULL DEFAULT ((0)),
[value] [nvarchar](255) NULL DEFAULT (NULL),
[id_doc] [bigint] NULL DEFAULT (NULL)
CONSTRAINT [PK_value_text_id] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
GO
ALTER TABLE [dbo].[value_text] WITH NOCHECK ADD CONSTRAINT [FK_valuesT_field] FOREIGN KEY([id_field])
REFERENCES [dbo].[fields] ([id])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[value_text] CHECK CONSTRAINT [FK_valuesT_field]
GO
Run Code Online (Sandbox Code Playgroud)
执行计划:
*我翻译了这个问题中的表格和字段以获得一般理解。在这张图片中,“archivos”相当于“files”、“clients”的“clientes”和“value_text”的“valores_texto”。
执行计划没有DISTINCT
:
没有DISTINCT
和的执行计划GROUP BY
(查询快一点):
这是比以前慢的查询的执行计划。在这里,查询返回了超过 400.000 行,但即使对结果进行分页,也没有变化。
更详细的执行计划:https : //www.brentozar.com/pastetheplan/?id=By_UC2aBG
这是比以前更快的查询的执行计划。在这里,查询返回超过 65.000 行。
更详细的执行计划:https : //www.brentozar.com/pastetheplan/?id=r116e6pSM
我认为你需要这个索引(正如Krismorte 建议的那样):
CREATE NONCLUSTERED INDEX [IX dbo.value_text id_file, id_field, value]
ON dbo.value_text (id_file, id_field, [value]);
Run Code Online (Sandbox Code Playgroud)
以下索引可能不是必需的,因为您似乎有一个合适的现有索引(问题中未提及),但为了完整起见,我将其包括在内:
CREATE NONCLUSTERED INDEX [IX dbo.files cid (id, year, name)]
ON dbo.files (cid)
INCLUDE
(
id,
[year],
[name]
);
Run Code Online (Sandbox Code Playgroud)
将查询表达为:
SELECT
FileId = F.id,
[FileName] = F.[name],
FileYear = F.[year],
V.[value]
FROM dbo.files AS F
JOIN dbo.clients AS C
ON C.id = F.cid
OUTER APPLY
(
SELECT DISTINCT
VT.[value]
FROM dbo.value_text AS VT
WHERE
VT.id_file = F.id
AND VT.id_field = 65739
) AS V
WHERE
C.id = 10
OPTION (RECOMPILE);
Run Code Online (Sandbox Code Playgroud)
这应该给出一个执行计划,如:
该OPTION (RECOMPILE)
是可选的。仅当您发现不同参数值的理想平面形状不同时才添加。对于此类“参数嗅探”问题,还有其他可能的解决方案。
使用新索引,您可能还会发现原始查询文本生成的计划非常相似,而且性能也不错。
您可能还需要更新files
表上的统计信息,因为提供的计划中的估计cid = 19
不准确:
更新文件表上的统计信息后,查询在所有情况下都运行得非常快。如果将来我在“文件”表中添加更多字段,我应该更新索引还是什么?
如果您向文件表添加更多列(并在查询中使用/返回它们),您将需要将它们添加到索引(至少作为包含的列)以保持索引“覆盖”。否则,优化器可能会选择扫描文件表而不是查找索引中不存在的列。您也可以选择cid
在该表上创建聚簇索引的一部分。这取决于。如果您想澄清这些要点,请提出一个新问题。