【问题标题】:Select data without SubQuery选择不带子查询的数据
【发布时间】:2013-04-24 19:01:57
【问题描述】:

我有两个名为 Test 的表,TestHistory 中的 testHistory 与 Test 有 FK 关系。

现在我想从带有测试表记录的表 TestHistory 中选择前 1 个插入的数据。

我想要的输出是:

或者我的 TestHistory 表没有必要有与 TestId 相关的记录,那么我的输出将是:

我已经通过子查询实现了这一点,例如:

Select Id,(select top(1) price from TestHistory HT Where T.Id=HT.TestId order by HT.Id desc) As Price 

From Test T

但我不想为此使用子查询。 我想在没有子查询的情况下选择数据。

请建议我如何在没有子查询的情况下做到这一点,解决此问题的最佳替代方法是什么。

【问题讨论】:

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


    【解决方案1】:

    我认为最简单的方法是使用row_number() 来获取结果,但您仍然会有一个子查询。不过,这将不是一个相关的子查询:

    select id, price, testhistoryid
    from
    (
      select t.id,
        h.price,
        h.id testhistoryid,
        row_number() over(partition by t.id order by h.id desc) rn
      from test t
      inner join testhistory h
        on t.id = h.testid
    ) src
    where rn = 1;
    

    SQL Fiddle with Demo

    如果您想返回历史记录表中不存在的test 行,那么您将使用LEFT JOIN 而不是INNER JOIN

    select id, price, testhistoryid
    from
    (
      select t.id,
        h.price,
        h.id testhistoryid,
        row_number() over(partition by t.id order by h.id desc) rn
      from test t
      left join testhistory h
        on t.id = h.testid
    ) src
    where rn = 1;
    

    Demo

    【讨论】:

    • 与简单的 subjqery(就像我写的)相比,这个查询在性能方面是否更好?
    • @bluefeet,我通常只会将testhistory 表放在子查询中,并在外部加入test(例如在我的回答中)。您是否发现优化器以与我建议的方式相同的方式处理您的子查询?
    • @ErikE 将它们都放在子查询中只是习惯,我现在没有能力对其进行测试以查看性能更好的方法
    【解决方案2】:

    很抱歉,如果没有某种子查询,就没有真正的方法可以做到这一点。你写的查询很好。在我看来,完成您正在寻找的最佳语法是这样的:

    SELECT
       T.ID,
       HT.*
    FROM
       dbo.Test T
       OUTER APPLY (
          SELECT TOP 1 *
          FROM dbo.TestHistory HT
          WHERE T.ID = HT.TestID
          ORDER BY HT.ID DESC
       ) HT
    ;
    

    Try this out yourself in a SQL Fiddle.

    如果您想限制它,只显示具有匹配 TestHistory 行的那些 Test 行,则使用 CROSS APPLY 而不是 OUTER APPLY

    此外,这样的查询是可能的并且可能更容易理解,但很可能不会执行得那么好,而且它要求每个 Test 行至少有一个 TestHistory 行。

    SELECT
       T.ID,
       HT.*
    FROM
       dbo.Test T
       INNER JOIN dbo.TestHistory HT
          ON T.ID = HT.TestID
    WHERE
       HT.ID = (
          SELECT Max(HT2.ID)
          FROM dbo.TestHistory HT2
          WHERE T.ID = HT2.TestID
       )
    ;
    

    您也可以使用Row_Number() 解决方案,但我倾向于不使用它,因为它的性能通常不如CROSS APPLYTOP(如果可用)。

    SELECT
       T.ID,
       HT.TestID,
       HT.Price
    FROM
       dbo.Test T
       LEFT JOIN (
          SELECT
             Selector = Row_Number() OVER (
                PARTITION BY HT.TestID ORDER BY HT.ID DESC
             ),
             *
          FROM dbo.TestHistory HT
       ) HT
          ON T.ID = HT.TestID
          AND HT.Selector = 1
    ;
    

    Try this out yourself in a SQL Fiddle.

    确定TOP 1Row_Number() 解决方案性能是否更好的唯一方法是使用大量具有适当代表性的数据尝试数据库中的每一个。如果您的历史记录条目非常少而主要条目非常多,您可能使用Row_Number() 获得更好的结果。但如果每个主条目都有很多历史条目,TOP 方法可能会执行得最好。

    表上的索引也可以产生巨大的影响。确保您拥有允许正确表访问的合理索引(例如,表TestHistory 上的聚集索引应该在TestID, ID 上,即使PK 在ID' 上也是如此)。如果您每次都查询整个表,索引可能没那么重要,但是当您有条件限制Test 行集时,索引将变得非常重要。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-14
      • 1970-01-01
      • 2021-03-26
      • 2011-07-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多