如果一个表有一个未索引列与索引列是一对多的关系,如何优化未索引列的查询?
Posted
技术标签:
【中文标题】如果一个表有一个未索引列与索引列是一对多的关系,如何优化未索引列的查询?【英文标题】:If a table has an unindexed column with a 1 to many relationship to an indexed column, how to optimize a query for the unindexed column? 【发布时间】:2019-10-16 04:49:36 【问题描述】:如果有一个包含足够记录的两列表 MyTable,则查询优化是相关的。
CorporationID int(未编入索引)
BatchID int(索引)
假设 CorporationID 和 BatchID 之间始终存在一对多的关系。换句话说,对于每个 BatchID,将只有一个 CorporationID,但对于每个 CorporationID,将有多个 BatchID 值。
我们需要获取所有公司 ID = 1 的 BatchID 值。
我知道最简单的解决方案可能是只为 CorporationID 添加一个索引,但假设不允许这样做,是否有其他方法可以通过查询或其他方式通知 SQL 每个 BatchID 仅对应于 1 个 CorporationID?
select distinct batchid from MyTable where corporationID = 1
好像没有效果。
select batchid from (select min(corporationid) corporationid, batchid
from MyTable group by batchid) subselect where corporationid = 1
这也是无效的,我认为是因为 SQL 需要不必要地遍历公司 ID 的所有值? (是否存在聚合函数来选择任何不会产生 min()、max()、sum() 或 avg() 开销的值??)
select batchid
from (
select corporationid, batchid
from (
select *, ROW_NUMBER() OVER (PARTITION BY batchid ORDER BY(SELECT NULL)) AS RowNumber
from mytable
) subselect
where RowNumber = 1
) subselect2
where corporationid = 1
这行得通吗?通过batchid无序分区后任意选择第1行相关的corporateid?
【问题讨论】:
你能解释一下你为什么不创建那个索引吗?这正是它的设计目的。这是一个纯粹的理论问题吗? 是的,这主要是一个理论问题,试图扩展我的 SQL 知识。实际上,我明天可能会将索引添加到表中,即使表很大,这让我有点犹豫是否要进行更改。 没有其他办法了。如果您想按没有索引的列进行选择,则必须对其进行扫描。 【参考方案1】:“假设不允许创建索引” - 这是一个极不可能的假设。当然,你应该创建索引。
对于您问题中的备选问题,最直接的答案是“否”。没有任何功能或子查询或视图或其他“读取”操作可用于获取给定 CorpID 的批次列表。您需要访问 corpID 数据才能做到这一点……您的所有示例查询都不起作用,因为在某些时候,它们需要访问 CorpID 以了解要为 BatchID 收集哪些行。任何可能存在的摘要或“汇总”功能仍然需要访问所有数据页面才能“查看”它们。书页的阅读是无法避免的。
如果不更改您的架构,就不可能进一步优化您的查询。
但是,通过一些更改,您可以有一些选项(但我想它们比仅仅添加索引要丑得多)。例如,您可以修改 BatchID 的结构以包含 BatchID 和 CorpID 的数据。像“8888899999999”这样的东西...... 9 是 batchID,8 是 CorpID。但这并没有给你带来太多好处,你没有节省任何索引空间,但至少你不必索引 CorpID 字段:) 可以这样做,但我不会分享任何其他内容。我不希望这里真正有经验的人看到这些东西而生病。 :)
如果你想提高性能,你需要一个 CorpID 上的索引。
【讨论】:
感谢您抽出宝贵时间回复。你的解释很有道理。理想情况下,我不会首先在每条记录中存储重复值,但不幸的是我无权修改访问现有数据结构的应用程序,因此我认为添加索引将是我的最佳选择。【参考方案2】:如果您没有大量数据,我建议在 Corporation ID 列上放置一个索引。但是如果数据太多,可以为每个Corporation ID定义一个索引
【讨论】:
我不知道“为每个公司 ID 定义索引”是什么意思?? 当你写信时:select * from mytable where corporationid = @corporationid
。您将在执行计划中看到从索引扫描(corporationid = @corporationid)直接读取速度相对较好
这意味着您应该在 CorporationId 上放置一个索引。我从您的回答中不明白为什么您有两种选择? “每个 CorporationID”相对于“CorporationID 上的索引”是什么意思??
创建索引时,就是在主表上创建了一个表。当索引基于公司 ID 时,此表较大。如果您需要每个公司 ID,则此表较小。为每个 Corporation ID 创建索引时,您只查找包含 Corporation ID 数据的表,而不是更多。
仍然是零意义。您能否使用显示您的意思的实际命令来更新您的答案。仍然不知道“公司 ID 上的索引”与“每个公司 ID 的索引”是什么意思。【参考方案3】:
第 01 部分=>
/*01Create DB*/
IF DB_ID('Test01')>0
BEGIN
ALTER DATABASE Test01 SET SINGLE_USER WITH ROLLBACK IMMEDIATE
DROP DATABASE Test01
END
GO
CREATE DATABASE Test01
GO
USE Test01
Go
第 02 部分=>
/*02Create table*/
CREATE TABLE Table01(
ID INT PRIMARY KEY IDENTITY,
Title NVARCHAR(100),
CreationDate DATETIME,
CorporationID INT ,
MyID INT ,
[GuidId1] [uniqueidentifier] NOT NULL,
[GuidId2] [uniqueidentifier] NOT NULL,
[Code] [nvarchar](50) NULL
)
ALTER TABLE [dbo].[Table01] ADD DEFAULT (GETDATE()) FOR [CreationDate]
GO
ALTER TABLE [dbo].[Table01] ADD DEFAULT (NEWSEQUENTIALID()) FOR [GuidId1]
GO
ALTER TABLE [dbo].[Table01] ADD DEFAULT (NEWID()) FOR [GuidId2]
GO
CREATE TABLE Table02(
ID INT PRIMARY KEY IDENTITY,
Title NVARCHAR(100),
CreationDate DATETIME,
CorporationID INT ,
MyID INT ,
[GuidId1] [uniqueidentifier] NOT NULL,
[GuidId2] [uniqueidentifier] NOT NULL,
[Code] [nvarchar](50) NULL
)
ALTER TABLE [dbo].[Table02] ADD DEFAULT (GETDATE()) FOR [CreationDate]
GO
ALTER TABLE [dbo].[Table02] ADD DEFAULT (NEWSEQUENTIALID()) FOR [GuidId1]
GO
ALTER TABLE [dbo].[Table02] ADD DEFAULT (NEWID()) FOR [GuidId2]
GO
第 03 部分=>
/*03Add Data*/
DECLARE @I INT = 1
WHILE @I < 1000000
BEGIN
DECLARE @Title NVARCHAR(100) = 'TITLE '+ CAST(@I AS NVARCHAR(10)),
@CorporationID INT = CAST((RAND()*20) + 1 AS INT),
@Code NVARCHAR(50) = 'CODE '+ CAST(@I AS NVARCHAR(10)) ,
@MyID INT = CAST((RAND()*50) + 1 AS INT)
INSERT INTO Table01 (Title , CorporationID , Code , MyID )
VALUES ( @Title , @CorporationID , 'CODE '+ @Code , @MyID)
SET @I += 1
END
INSERT INTO Table02 ([Title], [CreationDate], [CorporationID], [MyID], [GuidId1], [GuidId2], [Code])
SELECT [Title], [CreationDate], [CorporationID], [MyID], [GuidId1], [GuidId2], [Code] FROM Table01
第 04 部分=>
/*04 CREATE INDEX*/
CREATE NONCLUSTERED INDEX IX_Table01_ALL
ON Table01 (CorporationID) INCLUDE (MyID) ;
DECLARE @QUERY NVARCHAR(MAX) = ''
DECLARE @J INT = 1
WHILE @J < 21
BEGIN
SET @QUERY += '
CREATE NONCLUSTERED INDEX IX_Table02_'+CAST(@J AS NVARCHAR(5))+'
ON Table02 (CorporationID) INCLUDE (MyID) WHERE CorporationID = '+CAST(@J AS NVARCHAR(5))+';'
SET @J+= 1
END
EXEC (@QUERY)
第 05 部分=>
/*05 READ DATA => PUSH Button CTRL + M ( EXECUTION PLAN) */
SET STATISTICS IO ON
SET STATISTICS TIME ON
SELECT * FROM [dbo].[Table01] WHERE CorporationID = 10 AND MyID = 25
SELECT * FROM [dbo].[Table01] WITH(INDEX(IX_Table01_ALL)) WHERE CorporationID = 10 AND MyID = 25
SELECT * FROM [dbo].[Table02] WITH(INDEX(IX_Table02_10)) WHERE CorporationID = 10 AND MyID = 25
SET STATISTICS IO OFF
SET STATISTICS TIME OFF
注意 IO 、 TIME 和 EXECUTION PLAN 。 祝你好运
【讨论】:
这很有趣。 Sql Server 有一个特性,几百万年没人会用。如果你在研究大数据,你会发现你的愿景是数百万年前的。会计业务、银行业务等 用过这个功能。很抱歉浪费我的时间回复你以上是关于如果一个表有一个未索引列与索引列是一对多的关系,如何优化未索引列的查询?的主要内容,如果未能解决你的问题,请参考以下文章