это кандидат на групповой запрос sql? - PullRequest
2 голосов
/ 04 марта 2009

У меня есть две таблицы: 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 ]

5 голосов
/ 04 марта 2009

Нет, здесь нет необходимости выполнять 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
    )

Дополнительная GROUP BY необходима, только если у вас есть более одной записи в TableB с равными TableA_ID и равными DateStamp.


В приведенном вами конкретном примере запрос 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 неверен.

3 голосов
/ 04 марта 2009

Вы также можете попробовать функцию 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. Выбор последней даты с группировкой - с помощью RANK () OVER

1 голос
/ 04 марта 2009

Вы также можете сделать запрос, используя аналитические функции. В 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
0 голосов
/ 04 марта 2009

Вы не можете надежно получить более одного поля из таблицы 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
0 голосов
/ 04 марта 2009

, если вы хотите использовать группу, вы можете использовать:

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 с одинаковым значением DateStamp, ссылающимся на одну и ту же строку в таблицеA

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...