首页 > 解决方案 > 使用 SQL 根据第一个表从其他表中选择行

问题描述

我想将三个 T-SQL 语句合并为一个,因此它只是对数据库的一次调用,而不是三个。

SELECT * FROM Clients

第一个,从客户表中选择每个客户。

SELECT * FROM History

第二个,从历史表中选择所有历史条目。然后我使用一些代码来查找每个客户端的第一个历史记录。即表中的第一个历史记录ClientID被设置到该HasHistory列中ClientID

SELECT * FROM Actions

最后一个,我从动作表中获取所有动作。然后我使用一些代码来查找每个客户端的最后一个操作。即表中的最后一个动作ClientID被设置到那个LastAction列中ClientID

所以我想知道是否有办法编写这样的 SQL 语句?请注意,这不是真正的 SQL,只是用于说明我要实现的目标的伪代码。

SELECT * 
FROM Clients

AND 

SELECT First History Row 
FROM History 
WHERE History.ClientID = Clients.ClientID

AND 

SELECT Last Action Row 
FROM Actions 
WHERE Actions.ClientID = Clients.ClientID

标签: sql.netsql-server

解决方案


有很多方法可以做到这一点,但这里是一个例子。我会一点一点地解释我们在做什么。您尚未向我们展示表格设计,因此列名是猜测,但您应该明白了。

首先,您必须以某种方式标记您关心的历史记录行。一种方法是执行一个查询,在每个历史记录行上放置一个订单号,每个新客户从 1 开始,并按日期对它们进行排序。这样,每个客户端(您想要的那个)的第一个历史记录行总是有一个行号。这看起来像

SELECT
    *, 
    ROW_NUMBER() OVER (PARTITION BY clientID ORDER BY historyDate) AS orderNo 
FROM
    History

你会对动作做类似的事情,除了你想要最新的动作,而不是第一个,所以你的按列排序必须是相反的顺序 - 你通过告诉 ORDER BY 使用降序来做到这一点,像这样

SELECT 
    *, 
    ROW_NUMBER() OVER (PARTITION BY clientID ORDER BY actionDate DESC) AS orderNo 
FROM Actions

您现在应该有两个查询,其中您想要的唯一行被标记为订单号。您现在要做的是从您的第一个查询开始,并加入这两个其他查询,以便您只加入 orderno = 1 行。然后,您想要的所有数据将在一行中可用。您必须决定使用哪种联接类型 - 内部联接只会返回实际具有历史记录和操作的客户端。如果要查看其他表中根本没有行的客户端,则需要使用左外连接。但是你的最终查询(你只需要这个)看起来像

SELECT
    C.*, H.*, A.*
FROM
    Clients C
LEFT OUTER JOIN 
    (SELECT
         *, 
         ROW_NUMBER() OVER (PARTITION BY clientID ORDER BY historyDate) AS orderNo 
     FROM History) H ON H.clientID = C.clientID AND H.orderNo = 1
LEFT OUTER JOIN 
    (SELECT
         *, 
         ROW_NUMBER() OVER (PARTITION BY clientID ORDER BY actionDate DESC) AS orderNo 
     FROM Actions) A ON A.clientID = C.clientID AND A.orderNo = 1

这就是说:获取客户端(我们将其称为 C),然后对于每一行,尝试加入(匹配来自的行)我们在上面查看的历史查询(我们将其称为 H),其中客户端 ID相同,orderNo 为 1 - 即第一个历史记录行。它也对 Actions 查询执行相同的操作。


推荐阅读