tsql:在连接中选择子查询的替代方法

Posted

技术标签:

【中文标题】tsql:在连接中选择子查询的替代方法【英文标题】:tsql: alternative to select subquery in join 【发布时间】:2014-02-03 11:31:18 【问题描述】:

这是我简化的表格布局:

table1:pID(pkey),数据 table2:rowID(pkey)、pID(fkey)、数据、日期

我想从 table1 中选择一些行,将每个 pID 的 table2 中的一行连接到该 pID 的最近日期。 我目前使用以下查询执行此操作:

SELECT * FROM table1 as a
LEFT JOIN table2 AS b ON b.rowID = (SELECT TOP(1) rowID FROM table2 WHERE pID = a.pID ORDER BY date DESC)

这种工作方式很慢,可能是因为它必须对表 1 的每一行进行子查询。有没有办法提高性能或另一种方式?

【问题讨论】:

【参考方案1】:

您可以在这些行上进行尝试,使用子查询根据日期字段(按 pID 分组)获取最新的,然后将其与第一个表连接,这样子查询就不必执行对于 Table1 的 每一 行,将产生更好的性能:

Select *
FROM Table1 a
INNER JOIN 
   (
    SELECT pID, Max(Date) FROM Table2
    GROUP BY pID
   ) b
ON a.pID = b.pID

我已经使用 group by 为一列提供了示例 SQL,如果您需要其他列,请将它们添加到 GROUP BY 子句中。希望这会有所帮助。

【讨论】:

这确实快了很多。但是在 table2 中,我还需要返回一列,因此您的代码将变为:Select a.pID, a.data, b.data FROM Table1 a INNER JOIN ( SELECT pID, Max(Date), data FROM Table2 GROUP BY pID, data ) b ON a.pID = b.pID 这样我们将获得一个 pID 的多个值,我只需要最近的一个。 很高兴这有帮助:-)。是的,您需要添加您的案例所需的其他列。【参考方案2】:

使用下面的代码,并注意我添加了按 Date desc 的顺序来获取最近发送的数据

select *
from table1 a
inner join table2 b on a.pID=b.pID
where b.rowID in(select top(1) from table2 t where t.pID=a.pID order by Date desc)

【讨论】:

谢谢,我确实忘记了订单。但在执行时间它保持不变。 为了提高索引字段所需的性能,我忘记将 rowID 放在 select 中(select top(1) rowID from table2 t...)【参考方案3】:

我在类似的场景中使用下面的代码(我将其转录到您的示例中)

SELECT b.*
FROM table1 AS a
left outer join (
    SELECT a.*
    FROM table2 a
        inner join (
            SELECT a.pID, max(date) as date 
            FROM table2
            WHERE date <= <max_date>
            group by pID
            ) b ON a.pID = b.pID AND a.date = b.date
        ) b ON a.pID = b.pID
) b on a.pID = b.pID

这种方法的唯一问题是您必须确保日期不会重复 pID

【讨论】:

【参考方案4】:

您可以使用row_number() 函数和子查询来做到这一点:

SELECT t1.* 
FROM table1 t1 LEFT JOIN
     (select t2.*, row_number() over (partition by pId order by rowId desc) as seqnum
      from table2 t2
     ) t2
     on t1.pId = t2.pId and t2.seqnum = 1;

【讨论】:

这样更快并且得到正确的结果。谢谢 @jvcoppen 。 . .你有理由不接受这个答案吗?你可以接受任何你喜欢的答案,但我更喜欢这种方法而不是当前接受的答案。【参考方案5】:

使用 ROW_NUMBER() 函数获取一列,说明表 2 中每一行的哪个 id 是第一个(按 pID 分区,并按 rowDate 降序排序)

例子:

WITH cte AS 
(
  SELECT 
    rowID AS t2RowId, 
    ROW_NUMBER OVER (PARTITION BY pID ORDER BY rowDate DESC) AS rowNum
  FROM table2 t2
) -- gets the t2RowIds + a column which says which is the latest for each pID
SELECT t1.*, t2.* 
FROM table1 t1
LEFT JOIN 
(
  table2 t2
  JOIN cte ON t2.rowID = cte.t2RowId AND cte.rowNum = 1
) ON t1.pID = t2.pID

这保证每个 pID 仅返回 table2 中的 1 个项目,即使多个项目具有相同的日期。您当然应该确保在表 2 中对日期列进行索引,以便快速执行(理想情况下,索引还包括表 2 的 PrimaryID)

【讨论】:

以上是关于tsql:在连接中选择子查询的替代方法的主要内容,如果未能解决你的问题,请参考以下文章

在 MYSQL 的子查询中使用 LIMIT 关键字的替代方法

TSQL:SELECT CASE WHEN THEN 子查询:错误:子查询返回超过 1 个值

是否有比此查询更具可扩展性的子选择替代方案?

在 JPA Criteria API 的子查询中使用 ORDER BY 的替代方法是啥?

TSQL将子查询行值连接到列值中

使用子查询的替代方法 - 无法在 Mysql 中创建视图