我对Hibernate生成的不使用索引的查询有疑问。使用JTDS从Java访问数据库,服务器版本是SQL Server 2005 ,最新的服务包。
该字段是可空的,并且是一个外键,在某些特定情况下,可以完全为null,通过非聚集索引对列进行索引,但是当列完全为null时,从不使用索引,从而创建大量的全表扫描和性能问题。
使用带有以下SQL代码的标准查询分析器也可以验证这种情况:
创建表和索引
CREATE TABLE [dbo].[TestNulls](
[PK] [varchar](36) NOT NULL,
[DATA] [varchar](36) NULL,
[DATANULL] [varchar](36) NULL,
CONSTRAINT [PK_TestNulls] PRIMARY KEY NONCLUSTERED
(
[PK] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [IDX_DATA] ON [dbo].[TestNulls]
(
[DATA] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IDX_DATANULL] ON [dbo].[TestNulls]
(
[DATANULL] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
使用newid函数
填充一些随机数据declare @i as int
set @i = 0
while (@i < 500000)
begin
set nocount on
insert into TestNulls values(NEWID(), NEWID(), null)
insert into TestNulls values(NEWID(), null, null)
insert into TestNulls values(NEWID(), null, null)
set @i = (@i + 1)
set nocount on
end;
此查询执行全表扫描
declare @p varchar(36)
set @p = NEWID()
select PK, DATA, DATANULL from TestNulls
where DATANULL = @p
如果我使用“和DATANULL IS NOT NULL”完成查询,则查询现在使用索引。
需要帮助:
此致 马西莫
答案 0 :(得分:1)
1)我认为,我们应该避免使用NULL值。只需使用DEFAULT并将一些{00000-0000-000 ...}作为NULL值。您的数据填充脚本会生成太多的空值,因此该字段值的 selective 非常低。我认为SQL Server会选择扫描然后在这种情况下使用索引(SQL Server自动选择使用或不使用索引本身)。它很有意义。您应该分析 REAL 数据。任何方式你可以强制它只使用一些索引。你可以创建存储过程到sql server然后从hibernate查询它,例如, 或命令hibernate使用自定义查询来请求数据(我认为,这是可能的)并在查询中添加表提示以强制使用某些索引:
INDEX(index_val [,... n]):
select PK, DATA, DATANULL from TestNulls WITH INDEX(IDX_DATANULL)
选择性是“行数”/“基数”,所以如果你有10K客户,并搜索所有“女性”,你必须考虑搜索将返回10K / 2 = 5K行,因此选择性非常“差”。
运气。
答案 1 :(得分:0)
您正在使用没有聚簇索引的表(“堆表”,因为它被称为),这对于SELECT通常效率不高,因为任何有意义的查询都需要书签查找或全表扫描。
因此,要使用索引,服务器必须:1)在索引中查找给定值并检索相应的行ID,2)按ID检索行并返回数据。
鉴于数据的性质,优化器“认为”全扫描效率很高。
我建议你试试:
通过添加一些数据为此查询创建覆盖索引(它会使插入/更新稍慢,因此您应该考虑对系统的整体影响):
在TestNulls(DATANULL)上创建INDEX IDX_DATANULL_FULL包括(PK,DATA)