使用SQL Server 2005和Hibernate索引的问题

时间:2010-02-22 18:01:38

标签: sql-server-2005 hibernate jtds

我对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”完成查询,则查询现在使用索引。

需要帮助:

  • 如何强制JTDS / Hibernate组合使用索引(默认情况下sendStringParametersAsUnicode已设置为false)?
  • 是否有办法为使用可空字段的所有hibernate查询追加“且列不为空”?
  • 有关此行为的任何解释?

此致 马西莫

2 个答案:

答案 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检索行并返回数据。

鉴于数据的性质,优化器“认为”全扫描效率很高。

我建议你试试:

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

    在TestNulls(DATANULL)上创建INDEX IDX_DATANULL_FULL包括(PK,DATA)