【问题标题】:Rails: How do I create a clustered index on a non-id column in rails?Rails:如何在 rails 的非 id 列上创建聚集索引?
【发布时间】:2019-06-14 21:03:43
【问题描述】:

我想将时间序列数据存储在数据库中。数据将按如下方式组织:

  • 标题表包含有关数据集的信息(格式、来源等)。通常,一个数据集大约有 600 行。
  • “大数据”表包含实际的数据行。每行都有一个 header_id、时间戳和一些数据点。

我正在尝试在 data_header_id 列而不是 id 列上创建具有聚集索引的表。这样我就可以通过简单的SELECT * FROM big_datums WHERE data_header_id = 9001 ORDER BY timestamp ASC 获取所有数据点。

我有以下代码,但由于 Rails 的默认设置而出错:

class CreateBigData < ActiveRecord::Migration[5.2]
  def up
    create_table :headers do |t|
      t.string :data_format
      t.timestamps
    end

    create_table :big_datums do |t|
      t.references :data_header, null: false # This will need to be a CLUSTED index
      t.integer :timestamp
      t.integer :point1
      t.integer :point2
    end

    execute "CREATE CLUSTERED INDEX [data-header-index] ON [dbo].[big_datums] ( [data_header_id] ASC )
             WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF,
             ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]"
    end
  end

  def down
    execute "DROP INDEX [data-header-index] ON [dbo].[big_datums] WITH ( ONLINE = OFF )"
    drop_table :big_datums
    drop_table :headers
  end
end

我收到此错误:

ActiveRecord::StatementInvalid: TinyTds::Error: 无法在表 'dbo.big_datums' 上创建多个聚集索引。在创建另一个之前删除现有的聚集索引 'PK__build_te__3213E83F6568EFB2'。

这似乎是因为 rails 已经为 id 提供了一个集群 PK。

问题:

1) 如何按照说明创建表格?

2) 我还需要一个“id”列吗?我永远不会通过 ID 列进行查询,但拥有一个 PK 列来唯一标识记录可能仍然很好——尤其是因为 header_id + timestamp 可能不是一个好的集群 PK

【问题讨论】:

    标签: sql ruby-on-rails sql-server


    【解决方案1】:

    big_datums 上已经有一个clustered index,表上只能有clustered index,因为它定义了数据的物理结构(数据如何存储等)。您可以创建一个非聚集索引来支持您的查询,或者只保留现有索引。您可以拥有许多它们,它们是聚集索引/堆的附加结构。

    CREATE NONCLUSTERED INDEX [data-header-index] ON [dbo].[big_datums] ( [data_header_id] ASC )
    

    最好在您的clustered index 中有一个唯一键,因为在其他情况下,sql server 会为您的 8kb 页面增加一些开销以使行唯一。

    【讨论】:

    • 我将每天添加大约 150k 行。在 header_id 上使用聚集索引会不会提高空间/查找速度效率?
    • 如果 header_id 将用于连接,在哪里和查找它。听起来好像越来越多,所以也不错。
    【解决方案2】:

    您需要先删除 PK 约束。您可能应该将现有的聚集 PK 替换为 (data_header_id,id) 上的复合聚集索引。

    EG

    use tempdb 
    
    go
    drop table if exists big_datums 
    go
    
    create table big_datums
    (
      id int identity primary key,
      data_header_id int not null
    )
    
    go
    
    declare @pkname sysname = (select name from sys.key_constraints where type = 'PK' and parent_object_id = object_id('big_datums'))
    
    declare @sql nvarchar(max) = concat('alter table big_datums drop constraint [', @pkname,']')
    --print (@sql)
    exec (@sql)
    
    alter table big_datums
    add constraint pk_big_dautms
    primary key clustered (data_header_id,id)
    

    聚集索引在幕后始终是唯一的。 SQL Server 将在索引中添加一个隐藏的“uniqifier”列,以防出现重复。因此,如果您提供使其唯一的第二个键列,您最终会得到一个类似但更有用的聚集索引。如果您没有其他理由在id 上建立索引,您的表可以使用单一数据结构。

    【讨论】:

    • 哇,@David Browne!在 ID 和 header_id 上都有一个复合聚集索引有什么好处?典型的用例是拉入与指定 header_id 关联的所有数据 - 我看不出有任何理由仅通过其 id 拉入记录。
    • 太棒了-谢谢。单数据结构是什么意思?
    • 如果你在 data_header_id 上有一个聚集索引,在 id 上有一个主键,那么你将有两个 BTree 索引,每个索引一个。对表执行 DML 时,这两种数据结构都需要更新。
    猜你喜欢
    • 2021-01-14
    • 1970-01-01
    • 2013-01-24
    • 2012-08-26
    • 2017-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-23
    相关资源
    最近更新 更多