我有2个文件要导入MS SQL。第一个文件是2.2 GB,第二个文件是24 GB的数据。 (如果你很好奇:这是一张与扑克相关的查询表)
将它们导入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秒经过一些谷歌搜索后,我发现SQL服务器正在进行表扫描,这显然是一件坏事。我运行了“数据库引擎优化顾问”,它建议在flop_index列上创建一个索引(有意义)。创建索引后,数据库所需的磁盘空间正好翻了一倍! (加上日志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行。
EDIT2:更具体一点:是的,这是静态数据。我在二进制文件中有这些数据。我编写了一个程序,用几毫秒的时间查询我需要的数据。我想在数据库中使用这些数据的原因是我希望能够从网络中的不同计算机查询数据,而无需在每台计算机上复制25 GB。
HS意味着手力,它会告诉你当前的牌位与翻牌或转牌相结合的手牌强度。 ppot意味着积极的潜力,这是你的手在下一张普通牌被处理后领先的可能性。 hs1到9是1到9个对手的手势。对于ppot也是如此。动态计算ppot是非常密集的,需要花费几分钟来计算。我想创建一个扑克分析程序,它给出了每个可能的底牌组合的列表,在任何翻牌/转牌时都有他们的hs / ppot。
答案 0 :(得分:1)
要回答有关需要主键的问题 - 只提供您在问题中提供的信息:
根据您的表架构,您可以将其保留在那里。如果删除该标识列,则还将删除聚簇索引。您的聚簇索引值(4个字节)将作为指针存储在每个非聚集索引行中。通过删除该聚簇索引,您将把表作为堆离开 - 并且SQL将为表中的每一行创建一个8字节的RID(行标识符),并将其用作非聚集索引中的指针。因此,在您的情况下,基于您在问题中提供的模式 - 您可能会增加非聚集索引的大小,并最终减慢它们的速度。
所有人都说 - 根据您可能正在运行的查询(及其使用模式)未包含在问题中 - 将您的聚簇索引评估为身份列以外的其他内容也可以排成一行
答案 1 :(得分:1)
如果例如hs(X)和ppot(X)需要增长到9,那么你可以把表分成更小的表。
这就是你所拥有的:
[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?
答案 2 :(得分:0)
这是一个非常常见的问题。创建索引时,它可能会减少查询所需的时间,但会增加更新/插入所需的时间,还会增加每条记录所需的磁盘空间量。
如果索引为查询提供了性能提升,并且它保证了插入/更新性能和磁盘空间利用率的影响,则需要为每个列决定。
作为索引的替代方法,您可以使用OLAP cube。如果您的查询正在生成聚合或应用计算,那么您可能需要考虑每晚执行查询并将结果存储在不同的表中。您可以针对较小的表运行更简单的查询,并获得相同的结果,同时对性能的影响较小。
答案 3 :(得分:0)
你如何做索引和primkeys取决于。如果你只是想分析数据,如果你非常确定后续的DML命令只是SELECTs(没有INSERT),那么删除PK应该没问题。实际上,hand_id列是一个IDENTITY(自动增量)列,这意味着SQL Server无论如何都要管理该值(实际上,您不能在不使用之前切换IDENTITY_INSERT模式的额外麻烦的情况下将值插入该列中。开始你的INSERT语句,IIRC)。
当然,要警惕这个数据库不断变化的需求。如果需要改变,那么你应该考虑约束/索引/键。
如果将来考虑数据挖掘,请考虑使用Microsoft的SSAS(Analysis Services)。
更新:在阅读mayo的回复后,我同意索引(纯粹是为了速度,而不是约束强制执行)对于后续查询是可取的(回想一下,索引会加快读取操作,但通常会使插入/更新花费更长时间)。由于您的目标是执行单个批量插入,然后执行SELECT查询,您可以执行批量插入,然后在您的查询中可能候选的列上向数据库添加必要的索引。
答案 4 :(得分:0)
让我先说明我的回答是说将每种可能的组合放在数据库中会感觉不对。我会在一分钟内了解原因。
我从一张名为Cards的桌子开始。每张可能的卡都会有1条记录,它包括Suit,Face值,rank和yes的字段,CardID作为主键。同时索引诉讼和面值。
如果你想要列出每个可能的德州扑克牌,那么我会为pocketCards(pocketID,pCardID1,pCardID2),flopCards(flopID,fCardID1,fCardID2,fCardID3)制作单独的表,然后是TurnAndRiver的表格( turnAndRiverID,turnCardID,riverCardID)。然后是一个Hand表(handID,pocketID,flopID,turnAndRiverID,handScore)。
HandScore是一个基于表或标量值函数的计算字段。
通过分离这些位,可以避免大量复制,但您仍然需要担心卡的选择和重叠。
理想情况下,我会放弃手牌并计算手牌并在我正在构建的应用程序中得分以消耗此数据。
如果客户端要求您为奥马哈或五张牌抽牌建模,那么将太多的逻辑放在数据库中可能会使其难以适应。
根据您的索引问题,是的,我会使用主键,因为这样可以让您快速引用代码中的特定手牌。
更新
响应OP的编辑:听起来你正在使用错误的工具完成此任务。如果您总是要选择完全相同的记录集,那么在数据库中拥有数据的价值是什么?检查其他选项(例如,平面XML文件或代码中的静态DataSet)。它将为您节省连接时间和为基本上静态数据运行服务器的开销。