从大表中获取不同值的最佳方法

时间:2010-04-21 18:16:19

标签: sql-server performance

我有一个包含大约10个左右列的数据库表,其中两个是月份和年份。该表现在有大约250k行,我们预计它每月增长约100-150k。很多查询涉及月份和年份列(例如,2010年3月的所有记录),因此我们经常需要获得可用的月份和年份组合(即我们是否有2010年4月的记录?)。

同事认为我们应该有一个单独的表,我们的主表只包含我们有数据的月份和年份。我们每月只在主表中添加一次记录,因此只需在脚本末尾进行一次小更新即可将新条目添加到第二个表中。每当我们需要在第一个表上找到可用的月/年条目时,将查询第二个表。这个解决方案对我来说很糟糕,并且违反了DRY。

您认为解决此问题的正确方法是什么?有比两张桌更好的方法吗?

5 个答案:

答案 0 :(得分:13)

在所需列(年份和月份)上使用简单索引可以大大改善 DISTINCT GROUP BY 查询。< / p>

我不会使用辅助表,因为这会增加额外的头脑以维护辅助表(插入/更新删除将需要您验证辅助表)

修改

您甚至可以考虑使用Improving Performance with SQL Server 2005 Indexed Views

答案 1 :(得分:4)

确保在这些列上有一个Clustered Index。 并将这些日期列上的表分区为数据文件放在不同磁盘驱动器上的位置 我相信保持你的指数碎片低是最好的一击。

我也相信拥有所需选择的物理视图并不是一个好主意, 因为它增加了插入/更新开销。 平均每分钟有3,5个插入。 或每次插入之间约17秒(如果我错了,请平均纠正我)

问题是你每隔17秒选择一次吗? 这是关键的想法。 希望它有所帮助。

答案 2 :(得分:3)

使用“物化视图”,也称为“具有架构绑定的索引视图”,然后为此视图编制索引。执行此操作时,SQL Server将在后台创建并维护辅助表中的数据,并在适当时选择使用此表上的索引。

这与您的同事建议的类似,优点是您不需要为查询添加逻辑以利用它,SQL Server将在创建查询计划时执行此操作,SQL Server也将自动维护索引视图中的数据。

以下是如何完成此操作:创建一个视图,返回不同的[月] [年]值,然后在视图上索引[年] [月]。 SQL Server将再次使用视图上的微小索引,并避免在大表上进行表扫描。

因为SQL Server不允许使用DISTINCT关键字索引视图,而是使用GROUP BY [year],[month]并在SELECT中使用BIG_COUNT(*)。它看起来像这样:

CREATE VIEW dbo.vwMonthYear WITH SCHEMABINDING
AS

   SELECT
     [year],
     [month],
     COUNT_BIG(*) [MonthCount]
   FROM [dbo].[YourBigTable]
   GROUP BY [year],[month]
GO

CREATE UNIQUE CLUSTERED INDEX ICU_vwMonthYear_Year_Month 
   ON [dbo].[vwMonthYear](Year,Month)

现在,当您在大表上选择DISTINCT [Year],[Month]时,查询优化器将扫描视图上的微小索引,而不是扫描大表上的数百万条记录。

SELECT DISTINCT
   [year],
   [month]
FROM YourBigTable

这项技术使我获得了500万次读取,估计I / O为10.9到36次读取,估计I / O为0.003。这样做的开销是维护一个额外的索引,所以每次更新大表时,视图上的索引也会更新。

如果您发现此索引大大减慢了加载时间。删除索引,执行数据加载,然后重新创建它。

完整的工作示例:

    CREATE TABLE YourBigTable(
        YourBigTableID INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_YourBigTable_YourBigTableID PRIMARY KEY,
        [Year] INT,
        [Month] INT)
    GO  


    CREATE VIEW dbo.vwMonthYear WITH SCHEMABINDING
    AS

       SELECT
          [year],
          [month],
          COUNT_BIG(*) [MonthCount]
       FROM [dbo].[YourBigTable]
       GROUP BY [year],[month]
    GO

    CREATE UNIQUE CLUSTERED INDEX ICU_vwMonthYear_Year_Month ON [dbo].[vwMonthYear](Year,Month)


    SELECT DISTINCT
       [year],
       [month]
    FROM YourBigTable

-- Actual execution plan shows SQL server scaning ICU_vwMonthYear_Year_Month

答案 3 :(得分:1)

创建物化索引视图:

SELECT DISTINCT
    MonthCol, YearCol
    FROM YourTable

现在,您可以访问预先计算的不同值,而无需每次都完成工作。

答案 4 :(得分:1)

将日期作为表的聚簇索引键中的第一列。这对于历史数据来说非常典型,因为大多数(如果不是全部)查询都对特定范围感兴趣,并且聚合索引按时可以解决此问题。所有像“五月”这样的查询都需要作为范围来处理,例如:WHERE DATECOLKEY BETWEEN '05/01/2010' AND '06/01/2001'。回答诸如“5月是否有任何记录”之类的问题将涉及对聚集索引的简单搜索。

虽然这对于程序员来说似乎很复杂,但它是解决数据库设计问题的最佳方式。