2009-08-31 55 views
6

我有2个文件,我想导入到MS SQL中。第一个文件是2.2 GB,第二个文件是24 GB的数据。 (如果你很好奇:这是一个扑克相关的查找表)非常巨大的SQL数据库:模式应该如何?

将它们导入MS SQL不是问题。感谢SqlBulkCopy,我能够在短短10分钟内导入第一个文件。我的问题是,我不知道实际的表模式应该如何让我做一些非常快的查询。我第一次尝试天真看起来是这样的:

CREATE TABLE [dbo].[tblFlopHands](
    [hand_id] [int] IDENTITY(1,1) NOT NULL, 
    [flop_index] [smallint] NULL, 
    [hand_index] [smallint] NULL, 
    [hs1] [real] NULL, 
    [ppot1] [real] NULL, 
    [hs2] [real] NULL, 
    [ppot2] [real] NULL, 
    [hs3] [real] NULL, 
    [ppot3] [real] NULL, 
    [hs4] [real] NULL, 
    [ppot4] [real] NULL, 
    [hs5] [real] NULL, 
    [ppot5] [real] NULL, 
    [hs6] [real] NULL, 
    [ppot6] [real] NULL, 
    [hs7] [real] NULL, 
    [ppot7] [real] NULL, 
    [hs8] [real] NULL, 
    [ppot8] [real] NULL, 
    [hs9] [real] NULL, 
    [ppot9] [real] NULL, 
CONSTRAINT [PK_tblFlopHands] PRIMARY KEY CLUSTERED 
(
    [hand_id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY]

动态指数是从1到22100的值(前3张常见的德州扑克卡,52选3)。每个触发器索引都有一个从1到1176的hand_index(49选择2)。所以总共有25,989,600行在这张表中。

用我上面的“模式”做一个查询需要约。 25秒。经过一些Google搜索后,我发现SQL服务器正在进行表扫描,这显然是一件坏事。我运行了“数据库引擎优化顾问”,并建议在flop_index列上创建一个索引(合理)。创建索引后,DB所需的磁盘空间增加了一倍! (加上日志LDF文件增长了2.6 GB) 但是在索引之后,查询只花了几个毫秒。

现在我的问题是,我该如何正确地做到这一点?我从来没有用过这么庞大的数据,我之前创建的数据库只是一个笑话。

需要注意的一些事项:将数据导入MS SQL后,永远不会插入或更新数据,只需选择即可。所以我想知道我是否需要主键?

编辑:我提供一些更多的信息,使我的问题更加清晰:

1)我将永远不会使用hand_id。我只是把它放在那里,因为很久以前有人告诉我,我应该总是为每个表创建一个主键。

2)将基本只有一个查询,我将使用:

SELECT hand_index, hs1, ppot1, hs2, ppot2, hs3, ppot3, hs4, ppot4, hs5, ppot5, hs6, ppot6, hs7, ppot7, hs8, ppot8, hs9, ppot9 WHERE flop_index = 1...22100

此查询将始终与我所需要的数据返回1176行。

编辑2:只是更具体:是的,这是静态数据。我有这个数据在一个二进制文件。我已经编写了一个程序来在几毫秒内用我需要的数据查询这个文件。我想在数据库中使用这些数据的原因是,我希望能够从网络中的不同计算机查询数据,而无需在每台计算机上复制25 GB的数据。

HS表示手牌强度,它会告诉你当前手牌与翻牌或转牌相结合的手牌强度。 ppot意味着积极的潜力,这是一旦下一张普通卡被处理,你的手就会处于领先地位的机会。 hs1到9是对抗1到9个对手的手牌。同一个ppot。实时计算ppot非常密集,需要几分钟才能计算。我想创建一个扑克分析程序,该程序给出了在任何翻牌圈/转牌圈的每个可能的底牌组合以及他们的hs/ppot。

+5

只是为了将来的参考,这是一个小型SQL数据库,而不是一个巨大的;) – 2009-08-31 19:43:20

+2

嗯,它是*不*小。但无论如何,说数据库真的很大是主观的。有很多更大的数据库的例子。只要说出几千兆字节就可以了。 – 2009-08-31 19:45:00

+0

好吧,它可能不是一个巨大的谷歌数据库或类似,但对于一个宠物项目,我认为它是相当巨大的:) – Simon 2009-08-31 19:46:56

回答

0

这是一个很常见的问题。创建索引时,可能会缩短查询所需的时间,但会增加更新/插入所需的时间,并且还会增加每条记录所需的磁盘空间量。

如果索引为查询提供了性能提升,并且它确保影响插入/更新性能和磁盘空间利用率,则需要为每列确定一次。

作为索引的替代方法,您可能可以使用OLAP cube。如果您的查询正在生成聚合或应用计算,那么您可能需要考虑每晚执行查询并将结果存储在其他表中。您可以针对较小的表运行更简单的查询,并获得相同的结果,同时对性能影响较小。

0

你如何做你的索引和primkeys取决于。如果你只是想分析数据,并且你确信后续的DML命令只会是SELECT(不插入),那么删除PK应该没问题。实际上,hand_id列是一个IDENTITY(自动增量)列,这意味着SQL Server无论如何都管理该值(事实上,如果不在之前切换到IDENTITY_INSERT模式的额外麻烦中,则无法将值插入该列中开始你的INSERT语句,IIRC)。

当然,请谨慎对待这个数据库的不断变化的需求。如果需要改变,那么你应该考虑约束/索引/键。

如果将来要考虑数据挖掘,请考虑使用Microsoft的SSAS(分析服务)。更新:在阅读mayo的回复之后,我同意索引(纯粹是为了速度,而不是约束实施)对于后续查询是可取的(回想起索引加速读取操作但通常使插入/更新花费更长时间)。由于您的目标是执行一个批量插入,然后执行SELECT查询,因此您可以进行批量插入,然后将可能候选人的查询中所需的索引添加到数据库中。

+0

其实我根本不会使用hand_id。我创造了PK,因为我被教导要在每张桌子上总是创造一个PK。另外,在我的场景中,插入数据后永远不会有任何插入或更新。此外,我会一直使用hand_index查询,因此每个查询将返回1176行。那么在hand_index列上创建索引后,数据库大小加倍后,这是否正常?我认为这很奇怪,但如果它像这样工作,那就让它成为它吧。 – Simon 2009-08-31 19:57:49

1

要回答你的问题有关需要一个主键 - 只有你在问题中所提供的信息:

根据您的表模式,你还不如把它那里。如果你删除了这个标识列,你也会删除你的聚集索引。您的聚簇索引值(4个字节)作为指针存储在每个非聚簇索引行中。通过删除聚集索引,你可以将表格作为一个堆 - SQL将为表中的每一行创建一个8字节的RID(行标识符),并将其用作非聚集索引中的指针。所以,就你而言,根据你在问题中提供的模式 - 你可能会增加非聚集索引的大小,并最终减慢它们的速度。

由于所有的说 - 基于您可能正在运行的查询(及其使用模式)未包含在问题中 - 评估您的聚簇索引是标识列以外的内容可能与好。

1

那么,如果例如hs(X)和ppot(X)需要增长到九点以上,那么你可以将表格拆分成较小的表格。

这是你拥有的一切:

[hand_id] [int] IDENTITY(1,1) NOT NULL, 
    [flop_index] [smallint] NULL, 
    [hand_index] [smallint] NULL, 
    [hs1] [real] NULL, 
    [ppot1] [real] NULL, 
    etc... 

你可以把它分解成2个表(也许3如果需要)

Table hand: (EXAMPLE) 
[hand_id] [int] IDENTITY(1,1) NOT NULL, 
    [flop_index] [smallint] NULL, 
    [hand_index] [smallint] NULL 


Table hs_ppot (EXAMPLE) 
[hand_id] [int] IDENTITY(1,1) NOT NULL, 
[hs] [real] NULL, 
    [ppot] [real] NULL 

然后,你可以在每个表由hand_id引用。只是一个虽然。

BTW什么是hs和ppot?

+0

hs意味着Handstrength和ppot的意思是“积极的潜力” – Simon 2009-08-31 20:12:00

+0

好的谢谢,没有大的扑克 – 2009-08-31 20:23:30

+0

我实际上试图将数据分成多个表格,我会让你知道它是如何工作的。不幸的是,我并不擅长SQL;) – Simon 2009-08-31 20:36:56

0

让我先说一下我的回应,说把每一种可能的组合都放在数据库中感觉不对。我会在一分钟后回答为什么。

我会从名为卡片的桌子开始。每个可能的卡片将有1条记录,并且它将包括适合套牌,面值,等级和是的字段,CardID作为主要关键字。也索引诉讼,并面值。

如果你想列出每一个可能的德州扑克牌,那么我会为pocketCards(pocketID,pCardID1,pCardID2),flopCards(flopID,fCardID1,fCardID2,fCardID3)制作单独的表格,然后为TurnAndRiver (turnAndRiverID,turnCardID,riverCardID)。然后一个手表(handID,pocketID,flopID,turnAndRiverID,handScore)。

HandScore将是一个计算字段从表或标量值函数运行。

通过分离出这些位,可以避免大量的重复,但您仍然需要担心卡的选择和重叠。

理想情况下,我会放弃手表并计算手和得分的数据。

当您的客户要求您建模奥马哈或五张牌时,将过多的逻辑放在数据库中可能会使其很难适应。

对于您的索引问题,是的,我会使用主键,因为这可以让您快速引用代码中的特定手。

更新

为响应OP的编辑:这听起来像你正在使用该任务的错误的工具。如果您总是选择完全相同的记录集,那么在数据库中拥有数据的价值是什么?检查其他选项(例如,平面XML文件或代码中的静态DataSet)。它将为您节省连接时间和为本质上静态数据运行服务器的开销。