如何优化痛苦缓慢的SQL查询?

时间:2013-12-04 22:51:39

标签: sql sql-server

我有两张桌子。第一个“用户”表由记录用户登录事件的行组成。该表看起来像这样:

LoginTable
LoginID   Name    Time
100       Joe     12:00pm
101       Bob     3:30pm
102       Joe     4:00pm
103       Sue     6:15pm

第二个表记录了有关每次登录的其他指标,但并不常见,记录的其他项目数可能会发生变化。该表看起来像这样:

MetricsTable
MetricID  LoginID   Label       Data
500       100       IPAddress   1.2.3.4
501       100       Attempts    25       
502       100       Status      Good
503       101       IPAddress   1.2.3.5
504       101       Attempts    19       
505       101       Status      Bad
506       102       IPAddress   1.2.3.6
507       102       Attempts    35       
508       102       Status      Hold
509       103       IPAddress   1.2.3.7
510       103       Attempts    4       
511       103       Status      Trial

我正在尝试为每一行构建一个LoginTable查询,显示代表与每个登录事件相关的额外指标数据的其他列。

一种解决方案是做这样的事情:

SELECT LoginTable.LoginID, LoginTable.Name, LoginTable.Time, 
(SELECT MetricsTable.Data FROM MetricsTable WHERE
 MetricsTable.LoginID=LoginTable.LoginID and 
 MetricsTable.Label="IPAddress") as IPAddressEx
FROM LoginTable

该查询将显示一个额外的列,显示为该事件记录的“IPAddress”值。然后我可以再添加2个子查询来显示“尝试”和“状态”的其他列(注意这些只是假的例子)。我并不担心能够自动识别有更多可能的列,如果它们出现,我可以手动添加它们。但是,为每个额外列添加子查询的这种技术似乎非常缓慢。

有什么更好的方法来实现这一目标?

请假设我无法重组表格。

谢谢!

3 个答案:

答案 0 :(得分:0)

听起来你想要一个像这样的结果集:

LoginID   Name    Time   IP  Attempts  Status FutureColumn1 FutureColumn2 ...

您可以PIVOTDynamic PIVOT您的数据。

答案 1 :(得分:0)

Select  LoginID,
        Name,
        Time,
        [IPAddress],
        [Attempts],
        [Status]
From   (Select  LoginTable.LoginID, 
                LoginTable.Name, 
                LoginTable.Time, 
                MetricsTable.Data,
                MetricsTable.Label
        From    LoginTable
        Inner   Join MetricsTable
                ON  MetricsTable.LoginID=LoginTable.LoginID) As nested
Pivot  (Min(Data) For Label In ([IPAddress],[Attempts],[Status])) As PivotTable

我没有时间对此进行测试,而且我不是最好的即时临时支点,但我认为这是对的。

答案 2 :(得分:0)

与Andrew的答案类似,我会改为使用MetricsTable。

SELECT LoginTable.LoginID
    ,LoginTable.Name
    ,LoginTable.Time
    ,IPAddress= IPAddress.Data
    ,Attempts = Attempts.Data
FROM LoginTable
    Left Join MetricsTable As IPAddress
    On IPAddress.LoginID=LoginTable.LoginID 
    And IPAddress.Label="IPAddress"
    Left Join MetricsTable As Attempts
    On Attempts.LoginID=LoginTable.LoginID 
    And Attempts.Label="Attempts"
    -- Other joins for additional stats

如果您知道某些信息(如IPAddress)始终可用,请使用Inner Join代替Left Join

你可以按照Love2Learn的建议pivot数据,但我更喜欢直接加入。

此外,如果LoginID不是LoginTable上的PK,则其上的索引将提高读取速度。

同样,如果您没有MetricsTable.LoginID的索引,我强烈建议您这样做。 Foreign keys benefit from indexes

通过上述查询,MetricsTable上的有用索引将位于LoginID和Label上(按此顺序),并且可能包含Data,以便数据库引擎可以读取索引中的所有数据。您应该进行性能测试,看看索引的开销是否值得。