从庞大的数据库中选择行的最快方法是什么?

时间:2012-03-19 08:39:33

标签: sql sql-server sql-server-2008

我有一个超过300万行的巨大数据库(我的用户信息),我需要选择当天有生日的所有用户。

生日列是text(例如'19 / 03'或'19 / 1975/1975'),包含日期和月份,有时还有年份。

当我尝试选择具有左侧函数的行时,返回结果需要花费一分多钟时间。

我尝试为daymonthyear使用3个int列,然后进行选择,但需要更长时间才能得到结果。

关于如何让它跑得更快的任何想法?

我正在使用SQL Server 2008

由于

4 个答案:

答案 0 :(得分:2)

正如marc_s所提到的,如果可能的话,将其存储为日期类型 - 它将使SQL Server更快地执行比较,并且它将更容易维护。接下来,确保在该列上放置索引,并考虑包括任何额外的列,如果您只是查找生日以选择总行的一小部分。

最后 - 这是一个很大的问题。 TEXT是您可以选择的最差数据类型。存储TEXT的方式,数据实际上并不存储在页面本身上。相反,它留下了一个指向另一个页面的16字节指针。然后,该其他页面将在记录中包含数据本身。但是它变得更糟,当数据长度在0到64个字节之间时,该记录将是一个占用84字节空间的SMALL_ROOT数据类型!

因此,可以保存为8字节日期时间或4字节日期的内容现在占用总共100个字节,并导致每行的行外查找。基本上是糟糕表现的完美风暴。

如果您无法将其更改为更合适的日期时间,请至少将其更改为varchar!

答案 1 :(得分:1)

首先以SQL Server支持的格式保存日期,例如DATEDATETIME(在您的情况下,我猜DATE应该就够了)您可以使用MONTHDAY之类的SQL函数,如下所示,避免复杂的字符串操作函数,如LEFT等。

您的查询将如下所示:

select * from MyTable where MONTH(dateColumnA) = '1' && DAY(dateColumnB) ='7' --1 is for january

我不确定这是否会完全解决您的性能问题,但您可以在SQL查询分析器中运行此查询并查看它对索引等的推荐。我对日期类型的索引没有太多的了解列

答案 2 :(得分:0)

我要说的大部分内容都已经说过:使用DATE类型来存储日期,并确保将其编入索引。如果您要使用三个整数来存储日期并按其搜索,那么请确保它们也被编入索引:

CREATE INDEX IX_MyTable_Date_Ints ON MyTable(intYear, intMonth, intDay)
CREATE INDEX IX_MyTable_Date ON MyTable(BirthDate)

如果您希望能够在用户表中搜索除年份之外的生日,我建议使用固定年份将生日存储在不同的日期字段中,例如3004 - 而不是使用三个整数。您的基准年应为闰年,以满足可能在2月29日出生的任何人。如果您将来使用一年,您可以使用年份来确定日期实际上是应该忽略年份的日期。

然后你可以通过添加“WHERE birth_day ='3004-12-10'来搜索生日,无论年份如何,而不必对每条记录进行函数调用。如果这个字段被编入索引,你应该是能够在闪存中返回所有匹配的行。你需要记住,在搜索索引时,服务器最多需要进行32次比较才能找到40亿条记录中的匹配。永远不要低估索引的好处! / p>

我倾向于通过触发器保持生日,以便它保持自己更新。对于没有年份的出生日期,只需使用基准年(3004)。由于您的基准年是将来,您知道这个出生日期没有一年。

CREATE TABLE MyTable (
    MyTable_key INT IDENTITY(1, 1),
    username VARCHAR(30),
    birth_date DATE,
    birth_day DATE
)
ALTER TABLE MyTable ADD CONSTRAINT PK_MyTable PRIMARY KEY CLUSTERED (MyTable_key)
CREATE INDEX MyTable_birth_date ON MyTable(birth_date)
CREATE INDEX MyTable_birth_day ON MyTable(birth_day)
GO
CREATE TRIGGER tr_MyTable_calc_birth_day ON MyTable AFTER INSERT, UPDATE AS
    UPDATE t SET birth_day = DATEADD(YEAR, 3004-DATEPART(YEAR, t.birth_date), t.birth_date)
    FROM MyTable t, inserted i WHERE i.MyTable_key = t.MyTable_key

要更新现有表,请将更新作为独立查询运行,而不要像在触发器中使用那样连接到插入的表:

    UPDATE MyTable SET birth_day = DATEADD(YEAR, 3004-DATEPART(YEAR, birth_date), birth_date)

希望这有帮助。

答案 3 :(得分:0)

尝试使用Result Set而不是DataTable或DataSet。与这两个

相比,ResultSet很快