【问题标题】:sql subquery and join on one to many tablesql子查询并加入一对多表
【发布时间】:2012-10-19 18:52:01
【问题描述】:

这个太让我头疼了!基本上,我需要显示位置列表、存储在那里的当前项目以及拥有它的人。我创建了 4 个表并在其中填充了数据。

设置:MSSQL 2008

我正在寻找的是来自 tbl_locationHistory 的项目和所有者的“Top(1)”,因为在每个位置的任何时候都只能有一个项目或一个所有者。请记住,tbl_locationHistory 可能有多个数据位,我只需要为每个数据提取最新的 ID(例如,历史表中当前可用的最高 ownerID、最高 itemID)。

tbl_location

locID 本地名称

tbl_item

项目ID 物品名称

tbl_owner

所有者ID 所有者名称

tbl_locationHistory

​​>

locHistID 位置标识 物品编号 所有者ID 修改日期

我尝试使用连接将所有这些表连接在一起。但是我意识到这不起作用,因为我没有拉回“所有者”或“物品”的顶部 ID,因此显示的数据将不准确。

我想到的一件事是我应该将历史记录表分成两部分:1 x 所有者到位置和 1 x 项目到位置 - 但是我现在处于 SQL 的极限,所以可以使用一些指针正确的方向,不知道该走哪条路。

因此,即使我必须分解历史记录表,我也需要帮助。

2012 年 1 月 11 日更新

好的,我要在这里粘贴一些代码。

我是一个老派的经典 ASP 编码器.....还需要继续前进!我正在使用经典的 asp 和 MSSQL 来创建这个应用程序。我也在使用dreamweaver,因此希望将查询放在其中,这样我就可以使用DW CS6中的“绑定”功能。

这是我最初的查询:

SELECT DISTINCT p.pID, p.cliID, p.pNo, p.pName, p.dtCommissioned,     
p.description, ps.plotStatus, pt.plotType, o.oSalutation, o.oFname, o.oSname, man.manName, 
mod.model, sl.width, sl.length, sl.yr
FROM history AS ph INNER JOIN owners AS o ON ph.ownerID   
= o.ownerID INNER JOIN StockList AS sl ON ph.stockID = sl.stkID   
INNER JOIN manufacturers AS man ON sl.make = man.manID 
INNER JOIN models AS mod ON sl.model = mod.cID FULL OUTER   
JOIN plotType AS pt RIGHT OUTER JOIN                       
plots AS p ON pt.plotTypeID = p.plotType LEFT OUTER JOIN                       
plotStatus AS ps ON p.pStatus = ps.plotStatusID ON ph.plotID = p.pID
WHERE (p.cliID = value)
ORDER BY p.pNo

现在,当我意识到我没有正确地在历史表中查找“stockID”的最高 HistoryID 以及“ownerID”的最高 plotHistoryID 时,我开始研究如何执行“子查询”每个ID……这就是我卡住的地方。

我已经尝试了一些类似的东西:

Select p.pID, p.cliID, p.pNo, p.pName, p.dtCommissioned,     
p.description, ps.plotStatus, pt.plotType, o.oSalutation, o.oFname, o.oSname, man.manName, 
mod.model, sl.width, sl.length, sl.yr
(Select top 1 ph.plotHistoryID, ownerID
FROM History AS ph
order by ph.plotHistoryID desc)
THEN JOINING ETC
THEN DO ANOTHER SUBQUERY FOR THE OWNER ETC

但是我无法让它工作,但我现在超出了与子查询和联接相关的 SQL 水平。

输出显示将是:

情节编号 |地块名称 |情节类型 |地块状态 |当前所有者 |当前库存

每个绘图编号/名称/类型/状态都将填充我需要显示的数据。如果所有者\当前库存为空白,我有一个 IF 语句,说“没有所有者”或“没有找到库存”。

然后,用户将能够单击行末尾的“查看”链接并查看绘图/位置的详细信息,并将 pID 通过 QueryString 传递到下一页。

这有帮助吗?我现在有点绝望了!哈哈

【问题讨论】:

  • 如果我正确理解您想要做什么,那么加入就完全没有问题。您按 dateModified 排序的事实(您是,对吗?)意味着您将获得 tbl_locationHistory 的顶部,并且其他表将正确加入它

标签: sql sql-server-2008 join subquery one-to-many


【解决方案1】:

您可以使用ROW_NUMBER 函数来帮助仅获取每个位置的最新历史事件,然后使用它重新加入所有者和项目:

WITH LocationHistoryExt AS
(   SELECT  LocID, 
            ItemID, 
            OwnerID,
            DateModified,
            [RowNumber] = ROW_NUMBER() OVER(PARTITION BY LocID ORDER BY DateModified DESC)
    FROM    tbl_LocationHistory
)
SELECT  l.LocName,
        i.ItemID,
        o.OwnerName,
        lh.DateModified
FROM    tbl_Location l
        INNER JOIN LocationHistoryExt lh
            ON l.LocID = lh.LocID
            AND RowNumber = 1 -- ONLY LATEST EVENT
        INNER JOIN tbl_Item i
            ON lh.ItemID = i.ItemID
        INNER JOIN tbl_Owner o
            ON o.OwnerID = lh.OwnerID

编辑 - 添加说明

顶部是Common table Expression (CTE),在这种情况下,它只是将子查询移出主查询的一种方式,所以

WITH CTE AS
(   SELECT  A, B, C
    FROM    T
)
SELECT  *
FROM    CTE

和刚才执行完全一样

SELECT  A, B, C
FROM    T

CTE 中的 ROW_NUMBER 将根据其中的 ORDERPARTITION 子句为每个历史事件分配一个行号,因此在 tbl_LocationHistory 中给出以下示例数据:

locHistID | locID | itemID | ownerID | dateModified
----------+-------+--------+---------+--------------
    1     |   1   |   1    |   1     |  20121001
    2     |   1   |   4    |   2     |  20121002
    3     |   1   |   3    |   1     |  20121003  <-- LATEST FOR LocID 1
    4     |   2   |   5    |   2     |  20121002
    5     |   2   |   1    |   2     |  20121004
    6     |   2   |   2    |   3     |  20121005  <-- LATEST FOR LocID 2

我假设您要做的是检索每个位置 ID 的最新行,并获取与该行关联的 itemID 和 OwnerID,因此解构 ROW_NUMBER 函数:

ROW_NUMBER() OVER(PARTITION BY LocID ORDER BY DateModified DESC);

这将为数据中的每一行分配一个行号,对于每个 LocID (PARITION BY locID) 再次从 1 开始,它将从具有最新修改日期 (ORDER BY DateModified DESC) 的记录开始编号为 1。将此应用于您将获得的示例数据:

locHistID | locID | itemID | ownerID | dateModified | RowNumber
----------+-------+--------+---------+--------------+---------------
    1     |   1   |   1    |   1     |  20121001    | 3
    2     |   1   |   4    |   2     |  20121002    | 2
    3     |   1   |   3    |   1     |  20121003    | 1
    4     |   2   |   5    |   2     |  20121002    | 3
    5     |   2   |   1    |   2     |  20121004    | 2
    6     |   2   |   2    |   3     |  20121005    | 1

这是在我的查询中在 CTE 中检索的内容,然后当我加入它时,我添加了 RowNumber = 1,这意味着只返回最新的行

FROM    tbl_Location l
        INNER JOIN LocationHistoryExt lh
            ON l.LocID = lh.LocID
            AND RowNumber = 1 -- ONLY LATEST EVENT


locHistID | locID | itemID | ownerID | dateModified | RowNumber
----------+-------+--------+---------+--------------+---------------
    3     |   1   |   3    |   1     |  20121003    | 1
    6     |   2   |   2    |   3     |  20121005    | 1

剩下的只是正常的加入,从上面的 2 行中获取适当的 Location、Owner 和 Item。

我希望现在这更有意义。如果不让我知道,我会尝试解释任何其他内容。

编辑 2 - 对空字段进行会计处理

我稍微改变了select的顺序,把INNER JOIN改成了LEFT JOIN,但是主体还是一样的:

WITH LocationHistoryExt AS
(   SELECT  LocID, 
            ItemID, 
            OwnerID,
            DateModified,
            [RowNumber] = ROW_NUMBER() OVER(PARTITION BY LocID ORDER BY DateModified DESC)
    FROM    tbl_LocationHistory
)
SELECT  [LocationName] = COALESCE(l.LocName, 'No Location'),
        [ItemName] = COALESCE(i.ItemName, 'No Item'),
        [OwnerName] = COALESCE(o.OwnerName, 'No Owner'),
        lh.DateModified
FROM    LocationHistoryExt lh
        LEFT JOIN tbl_Location l
            ON l.LocID = lh.LocID
        LEFT JOIN tbl_Item i
            ON lh.ItemID = i.ItemID
        LEFT JOIN tbl_Owner o
            ON o.OwnerID = lh.OwnerID
WHERE   RowNumber = 1 -- ONLY LATEST EVENT

【讨论】:

  • 嗨,加雷斯。感谢您的评论,但这对我没有多大帮助......我不明白你写的是什么!?!抱歉,这是我的编码能力水平。我正在构建一个 Web 应用程序,但大部分查询都非常简单,带有标准的子查询。现在我正在寻找将它们全部整合在一起的方法,我感到有些困惑。我需要能够从位置中选择 *,显示该位置的当前项目和项目的当前所有者.....后面的 2 个绑定将作为 ID 出现在历史表中......这有帮助吗你还有吗?或者其他任何人?
  • 好吧,我的查询工作正常,这是个好消息!但是,据我了解,从我所看到的结果来看,它只会拉回所有匹配的行。这对一份报告来说很好,但我还想显示项目在哪里是空的并且没有应用所有者.....我有空字段的 0(零)而不是历史表中的 NULL 值,如果这对你有帮助的话.我想我应该有更多的帮助!对于返回为零的字段,我将在 GUI 中用 IF 语句等替换为“no owner”和“no stock”的字段。
  • @NickGreen 进一步编辑的答案有望解决问题。
  • 我明白你在做什么,我非常感谢详细的帮助。然而,这似乎只返回一行数据。当我运行 SQL 时,它在转换时出错 - i.ItemName 不是我持有的东西。此外,它是我从另外两个表中引用的一个 ID……一个用于制造,一个用于模型……这对事情有什么启发吗?
  • 对不起,忘记在上面的评论中输入你的名字了。在此先感谢,尼克
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多