0

我对 Hibernate 生成的不使用索引的查询有疑问。使用 JTDS 从 Java 访问数据库,服务器版本是 SQL Server 2005,最新的服务包。

该字段可以为空,并且是一个外键,在某些特定情况下,可能完全为空,列通过非聚集索引建立索引,但当列完全为空时从不使用索引,从而创建大量全表扫描和性能问题。

也可以使用带有以下 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

如果我使用“and DATANULL IS NOT NULL”完成查询,则查询现在使用索引。

需要帮助:

  • 如何强制 JTDS/Hibernate 组合使用索引(默认情况下 sendStringParametersAsUnicode 已设置为 false)?
  • 有没有办法为所有使用可空字段的休眠查询附加“并且列不为空”?
  • 关于这种行为的任何解释?

问候马西莫

4

2 回答 2

1

1)我认为,我们应该避免使用 NULL 值。只需使用 DEFAULT 并将一些 {00000-0000-000...} 作为 NULL 值。您的数据填充脚本生成的空值过多,因此该字段的值的选择性非常低。我认为在这种情况下SQL Server会选择扫描然后使用索引(SQL Server会自动选择使用或不使用索引本身)。这是有道理的。你应该分析你的真实数据。任何方式你都可以强制它只使用一些索引。例如,您可以为 sql server 创建存储过程,然后从 hibernate 查询它,或者命令 hibernate 使用自定义查询来请求数据(我认为,这是可能的)并向查询添加表提示以强制使用某些索引:

索引 ( index_val [ ,...n ] ):

select PK, DATA, DATANULL from TestNulls WITH INDEX(IDX_DATANULL)

选择性是“行数”/“基数”,所以如果您有 10K 客户,并且搜索所有“女性”,您必须考虑搜索将返回 10K/2 = 5K 行,所以非常“糟糕” “选择性。

运气。

于 2010-02-27T21:55:21.627 回答
0

您正在使用没有聚集索引的表(所谓的“堆表”),这对于 SELECT 来说通常不是很有效,因为任何有意义的查询都需要书签查找或全表扫描。

因此,要使用索引,服务器必须:1)在索引中找到给定的值并检索相应的行 ID,2)按 ID 检索行并返回数据。

考虑到数据的性质,优化器“认为”全扫描效率更高。

我建议你尝试:

  1. 重建表上的统计信息。过时的统计数据可能会导致优化器做出错误的决定。
  2. 通过提示强制使用索引。不要忘记测试它是否真的在您的实际数据上更快(有时优化器碰巧比您更了解)。
  3. 通过添加一些数据为这个查询创建一个覆盖索引(它会使插入/更新有点慢,所以你应该考虑对系统的整体影响):

    创建索引 IDX_DATANULL_FULL ON TestNulls (DATANULL) INCLUDE (PK, DATA)

于 2010-03-02T10:13:28.037 回答