在工作中,我们构建了大型多页面Web应用程序,主要包括无线电和复选框。每个应用程序的主要目的是收集数据,但当用户返回他们之前访问过的页面时,我们会向他们报告他们之前的回复。在最坏情况下,我们可能有多达900个不同的变量和大约150万用户。
由于多种原因,使用仅插入方法来存储数据(而不是就地更新)是有意义的,这样我们就可以捕获有关与变量重复交互的历史数据。最终结果是每个变量每个用户可能有几个响应。
我们收集回复的表格看起来像这样:
CREATE TABLE [dbo].[results](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[userid] [int] NULL,
[variable] [varchar](8) NULL,
[value] [tinyint] NULL,
[submitted] [smalldatetime] NULL)
其中id作为主键。
实际上每个请求都会产生一系列插入语句(每个变量提交一个),然后我们运行一个select来生成下一页的上一个响应(如下所示):
SELECT t.id, t.variable, t.value
FROM results t WITH (NOLOCK)
WHERE t.userid = '2111846' AND
(t.variable='internat' OR t.variable='veteran' OR t.variable='athlete') AND
t.id IN (SELECT MAX(id) AS id
FROM results WITH (NOLOCK)
WHERE userid = '2111846' AND (t.variable='internat' OR t.variable='veteran' OR t.variable='athlete')
GROUP BY variable)
在这种情况下,对于用户2111846,将返回变量“internat”,“veteran”和“athlete”的最新响应。
我们在索引表格时遵循了数据库调优工具的建议,而对于我们的数据,这是我们能够提出的最佳性能的选择查询版本。即便如此,由于该表接近100万条记录(而且我们可能有大约150倍的记录),似乎会有显着的性能下降。我们有一个相当优雅的解决方案,用于分割多个表格中的数据,这些表格一直运行良好,但我愿意接受有关如何构建更好版本的select查询的任何建议。我们经常使用这种结构来存储大量独立的数据点,我们喜欢它提供的好处。
所以问题是,如何提高选择查询的性能?我假设嵌套的select语句是一个坏主意,但我还没有找到一个同样可以执行的替代方法。
提前致谢。
注意:由于我们强调在这种情况下创建过度阅读,并且因为我们从未更新过,所以在这种情况下使用NOLOCK指令似乎没有任何惩罚(和一些优势)。
答案 0 :(得分:3)
请勿使用NOLOCK提示。请改用snapshot isolation,只需在数据库上启用READ_COMMITED_SNAPSHOT ON。
另外,你说你想要“最近的”,但你选择了ID by ID,这是一个IDENTITY键。你不应该使用'最近'的日期时间吗?标识只会将插入顺序设为“最近”,这可能对域模型没有意义。
要返回您想要的数据,最好的方法是创建适当的群集密钥:
CREATE TABLE [dbo].[results](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[userid] [int] NULL,
[variable] [varchar](8) NULL,
[value] [tinyint] NULL,
[submitted] [smalldatetime] NULL);
create clustered index cdxResults on results ([userid], variable, id DESC);
如果您有一个可能的变量类型表,那么可以通过在APPLY运算符中使用MAX来高度优化查询:
SELECT t.id, t.variable, t.value
FROM (
SELECT 'internat' as variable
UNION ALL SELECT 'veteran'
UNION ALL SELECT 'athlete'
) as v
CROSS APPLY (
SELECT TOP(1) id, variable, value
FROM results
WHERE userid = @userid
AND variable = v.variable
ORDER BY id DESC
) as t
此查询将运行3搜索到聚簇索引,响应时间将始终为常量O(1),与数据大小无关。 IE浏览器。同一时间表中的100行,100万或10亿。