这是一个查询sql组的候选人?

时间:2009-03-04 09:25:35

标签: sql

我有两个表: TableA ID [int, pk], Name [string]) TableB (ID [int, pk], TableA_ID [int, fk], Name [string], DateStamp [datetime (dd/mm/yyyy hh:MM:ss)])。TableA和TableB之间存在一对多的关系

两个表上的内部联接将给出以下结果:

TableA.ID, TableA.Name, TableB.Name, TableB.DateStamp
        1,    'File A', 'Version 1', 01/01/2009 15:00:00
        1,    'File A', 'Version 2', 05/01/2009 08:15:00
        1,    'File A', 'Version 3', 06/01/2009 19:33:00
        2,    'File B', 'Version 1', 03/01/2009 09:10:00
        2,    'File B', 'Version 2', 20/01/2009 20:00:00
        3,    'File C', 'Version 1', 01/01/2009 17:00:00

我真正想要的是以下内容(TableA中的每一行和TableB中的最后一个匹配行):

TableA.ID, TableA.Name, TableB.Name, TableB.DateStamp
        1,    'File A', 'Version 3', 06/01/2009 19:33:00
        2,    'File B', 'Version 2', 20/01/2009 20:00:00
        3,    'File C', 'Version 1', 01/01/2009 17:00:00

这是我用来实现此目的的查询:

SELECT ta.ID, ta.Name, tb.Name, tb.DateStamp
FROM TableA ta INNER JOIN TableB tb ON ta.ID = tb.TableA_ID
WHERE tb.ID IN (
 SELECT TOP 1 tb2.ID 
 FROM TableB tb2 
 WHERE tb2.TableA_ID = ta.ID 
 ORDER BY tb2.DateStamp DESC)

这有效,但我的直觉是,我不是以“最好的方式”做到这一点。看起来它是聚合查询(即groupby)的候选者,但我没有任何运气。最后,我总是不得不使用子查询来获取我在TableB中的行。

任何帮助都非常感激。

5 个答案:

答案 0 :(得分:5)

不,这里不需要进行GROUP BY,这应该通过相关的子查询来解决:

SELECT
  TableA.ID, 
  TableA.Name, 
  TableB.Name, 
  TableB.DateStamp
FROM
  TableA
  INNER JOIN TableB ON 
    TableA.ID = TableB.TableA_ID
    AND TableB.DateStamp = (
      SELECT MAX(DateStamp) 
      FROM TableB
      WHERE TableA_ID = TableA.ID
    )

只有TableB中的多条记录与TableA_ID 相等且等于DateStamp时,才需要额外的GROUP BY。


对于您显示的具体示例,GROUP BY查询恰好产生正确的结果。这仍然是错误的,因为在这种情况下,正确的结果更具副作用。

SELECT
  TableA.ID, 
  TableA.Name, 
  MAX(TableB.Name) Max_TableBName, 
  MAX(TableB.DateStamp) Max_TableBDateStamp
FROM
  TableA
  INNER JOIN TableB ON TableA.ID = TableB.TableA_ID
GROUP BY
  TableA.ID, 
  TableA.Name

这取决于MAX(TableB.Name)实际上是您想要获得的值的巧合,并且它与MAX(TableB.DateStamp)对齐。但由于这种相关性仅仅是偶然事件,因此GROUP BY查询是错误的。

答案 1 :(得分:3)

你也可以尝试RANK()OVER功能:

-- Test data
DECLARE @TableA TABLE (ID INT, Name VARCHAR(20))
INSERT INTO @TableA
SELECT 1, 'File A' UNION
SELECT 2, 'File B' UNION
SELECT 3, 'File C'

DECLARE @TableB TABLE (ID INT, TableAID INT, Name VARCHAR(20), 
  DateStamp DATETIME)
INSERT INTO @TableB
SELECT 1, 1, 'Version 1', '01/01/2009 15:00:00' UNION
SELECT 2, 1, 'Version 2', '01/05/2009 08:15:00' UNION
SELECT 3, 1, 'Version 3', '01/06/2009 19:33:00' UNION
SELECT 4, 2, 'Version 1', '01/03/2009 09:10:00' UNION
SELECT 5, 2, 'Version 2', '01/20/2009 20:00:00' UNION
SELECT 6, 3, 'Version 1', '01/01/2009 17:00:00'

-- Actually answer
SELECT M.ID, M.AName, M.BName, M.DateStamp FROM
(   SELECT RANK() OVER(PARTITION BY A.ID ORDER BY B.DateStamp DESC) AS N, 
    A.ID, A.Name AS AName, B.Name AS BName, B.DateStamp
    FROM @TableA A INNER JOIN @TableB B ON A.ID = B.TableAID
) M WHERE M.N = 1

请参阅2. Last Date selection with grouping - using RANK() OVER

答案 2 :(得分:1)

您还可以使用分析功能进行查询。在Oracle中,您可以这样做:

select distinct
       A.Id
,      A.Name
,      first_value(B.Name)      over (partition by B.id
                                      order     by B.DateStamp desc)   BName
,      first_value(B.DateStamp) over (partition by B.id
                                      order     by B.DateStamp desc)   DateStamp
from   TableA A inner join TableB B  on A.id = B.id

答案 3 :(得分:0)

如果你想使用group by,你可以使用:

select 
    ta.id, ta.name, tb.name, tb.dateStamp
from 
    tableA ta 
    inner join tableB tb on ta.id = tb.tablea_id
    inner join (
        select tablea_id, max(DateStamp) as maxDateStamp from tableB
        group by tablea_id
    ) latestB 
        on tb.tablea_id = latestB.tablea_id
        and tb.DateStamp = latestB.maxDateStamp

但是如果tableB中有多个条目,并且在tableA中引用相同行的DateStamp值相同,我将返回多个记录

答案 4 :(得分:0)

您无法在分组中从B表中可靠地获取多个字段,但您可以根据结果加入B表以获取其他字段:

select x.ID, x.Name, b.Name, b.DateStamp
from (
  select a.ID, a.Name, max(b.DateStamp) as DateStamp
  from TableA a
  inner join TableB b on b.TableA_ID = a.ID
  group by a.ID, a.Name
) x
inner join TableB b on b.TableA_ID = x.ID and b.DateStamp = x.DateStamp