Как выбрать только верхнюю и нижнюю строки из таблицы - PullRequest
3 голосов
/ 27 сентября 2019

Я пытаюсь выбрать верхнюю и нижнюю строку из таблицы, как показано ниже, но получаю сообщения об ошибках:

Msg 156, Level 15, State 1, Line 22
Incorrect syntax near the keyword 'UNION'

Как правильно использовать 'UNION'?

Use DATABASE
Go
declare @Volume VARCHAR(512)
set @Volume = '599722'


SELECT    
TOP (1)
[ARCHIVE_HEADER].archive as FirstArchive,  
[ARCHIVE_HEADER].cr_time,
[ARCHIVE_HEADER].group_name,
[VOLUME].vol_name,
[ARCHIVE_HEADER].display_name
FROM            
ARCHIVE_HEADER INNER JOIN VOLUME
ON ARCHIVE_HEADER.volume_key = VOLUME.volume_key
WHERE        
VOLUME.vol_name = @Volume
ORDER BY
ARCHIVE_HEADER.archive ASC

UNION

SELECT    
TOP (1)
[ARCHIVE_HEADER].archive as FirstArchive,  
[ARCHIVE_HEADER].cr_time,
[ARCHIVE_HEADER].group_name,
[VOLUME].vol_name,
[ARCHIVE_HEADER].display_name
FROM            
ARCHIVE_HEADER INNER JOIN VOLUME
ON ARCHIVE_HEADER.volume_key = VOLUME.volume_key
WHERE        
VOLUME.vol_name = @Volume
ORDER BY
ARCHIVE_HEADER.archive DESC

С точки зрения производительности, это должно запрашивать одну и ту же таблицу дважды для результата, есть ли лучшая логика может запрашивать один и тот же результат только один раз?

Ответы [ 4 ]

2 голосов
/ 27 сентября 2019

Я думаю, это будет проще, если использовать один запрос:

SELECT FirstArchive, cr_time, group_name, vol_name, display_name
FROM (SELECT ah.archive as FirstArchive, ah.cr_time, ah.group_name,
             v.vol_name, ah.display_name,
             ROW_NUMBER() OVER (PARTITION BY v.vol_name ORDER BY ah.archive ASC) as seqnum_asc,
             ROW_NUMBER() OVER (PARTITION BY v.vol_name ORDER BY ah.archive DESC) as seqnum_desc
      FROM ARCHIVE_HEADER ah INNER JOIN
           VOLUME v
           ON ah.volume_key = v.volume_key
      WHERE v.vol_name = @Volume
     ) ah
WHERE 1 IN (seqnum_asc, seqnum_desc)
ORDER BY ah.archive ASC;

Нет необходимости UNION ALL два подзапроса.

Примечание. Это имеет одно отличие от вашего запроса,Если для volume есть только одна строка, то возвращается одна строка, а не две.Это похоже на улучшение.

2 голосов
/ 27 сентября 2019

использовать подзапрос

    select * from (
SELECT    
    TOP (1)
    [ARCHIVE_HEADER].archive as FirstArchive,  
    [ARCHIVE_HEADER].cr_time,
    [ARCHIVE_HEADER].group_name,
    [VOLUME].vol_name,
    [ARCHIVE_HEADER].display_name
    FROM            
    ARCHIVE_HEADER INNER JOIN VOLUME
    ON ARCHIVE_HEADER.volume_key = VOLUME.volume_key
    WHERE        
    VOLUME.vol_name = @Volume
    ORDER BY
    ARCHIVE_HEADER.archive ASC
) a        
    UNION
 select * from (        
    SELECT    
    TOP (1)
    [ARCHIVE_HEADER].archive as FirstArchive,  
    [ARCHIVE_HEADER].cr_time,
    [ARCHIVE_HEADER].group_name,
    [VOLUME].vol_name,
    [ARCHIVE_HEADER].display_name
    FROM            
    ARCHIVE_HEADER INNER JOIN VOLUME
    ON ARCHIVE_HEADER.volume_key = VOLUME.volume_key
    WHERE        
    VOLUME.vol_name = @Volume
    ORDER BY
    ARCHIVE_HEADER.archive DESC
) b
1 голос
/ 27 сентября 2019

В качестве альтернативы union вы можете использовать min и max в подзапросе, а затем использовать внутреннее соединение. непроверено

select [ARCHIVE_HEADER].archive,  
   [ARCHIVE_HEADER].cr_time,
   [ARCHIVE_HEADER].group_name,
   [VOLUME].vol_name,
   [ARCHIVE_HEADER].display_name
from ARCHIVE_HEADER
inner join (
   select min(archive) FirstArchive, max(archive) LastArchive
   from ARCHIVE_HEADER
   inner join VOLUME on ARCHIVE_HEADER.volume_key = VOLUME.volume_key
   where VOLUME.vol_name = @Volume
) MinMax_Archive
   on [ARCHIVE_HEADER].archive = FirstArchive
   or [ARCHIVE_HEADER].archive = LastArchive
inner join VOLUME on ARCHIVE_HEADER.volume_key = VOLUME.volume_key
where VOLUME.vol_name = @Volume
order by [ARCHIVE_HEADER].archive

Для нескольких томов ВСЕ тома должны содержать список: еще больше непроверенных

select [ARCHIVE_HEADER].archive,  
   [ARCHIVE_HEADER].cr_time,
   [ARCHIVE_HEADER].group_name,
   [VOLUME].vol_name,
   [ARCHIVE_HEADER].display_name
from ARCHIVE_HEADER
inner join VOLUME on ARCHIVE_HEADER.volume_key = VOLUME.volume_key
inner join (
   select volume_key min(archive) FirstArchive, max(archive) LastArchive
   from ARCHIVE_HEADER
   group by volume_key
) MinMax_Archive
   on ARCHIVE_HEADER.volume_key = MinMax_Archive.volume_key
   and ( [ARCHIVE_HEADER].archive = FirstArchive
         or [ARCHIVE_HEADER].archive = LastArchive)
order by vol_name, [ARCHIVE_HEADER].archive

Чтобы ограничить его некоторымитома, вы можете перечислить их напрямую, используя IN:

select [ARCHIVE_HEADER].archive,  
   [ARCHIVE_HEADER].cr_time,
   [ARCHIVE_HEADER].group_name,
   [VOLUME].vol_name,
   [ARCHIVE_HEADER].display_name
from ARCHIVE_HEADER
inner join VOLUME on ARCHIVE_HEADER.volume_key = VOLUME.volume_key
inner join (
   select volume_key min(archive) FirstArchive, max(archive) LastArchive
   from ARCHIVE_HEADER
   group by volume_key
) MinMax_Archive
   on ARCHIVE_HEADER.volume_key = MinMax_Archive.volume_key
   and vol_name in ('599722', '458933', '342902')
   and ( [ARCHIVE_HEADER].archive = FirstArchive
         or [ARCHIVE_HEADER].archive = LastArchive)
order by vol_name, [ARCHIVE_HEADER].archive

Если вы хотите использовать переменную для нескольких томов, то это немного сложнее.Вы захотите создать табличную переменную (или временную таблицу) из vol_name, которую вы хотите, а затем внутренне соединить эту таблицу с остальными.

1 голос
/ 27 сентября 2019

Интересный вопрос ...

Вы можете использовать временную таблицу.Что-то вроде:

declare @Volume VARCHAR(512)
set @Volume = '599722'


SELECT    
TOP (1)
[ARCHIVE_HEADER].archive as FirstArchive,  
[ARCHIVE_HEADER].cr_time,
[ARCHIVE_HEADER].group_name,
[VOLUME].vol_name,
[ARCHIVE_HEADER].display_name
INTO #t --this step creates the temp table
FROM            
ARCHIVE_HEADER INNER JOIN VOLUME
ON ARCHIVE_HEADER.volume_key = VOLUME.volume_key
WHERE        
VOLUME.vol_name = @Volume
ORDER BY
ARCHIVE_HEADER.archive ASC

INSERT INTO #t
SELECT    
TOP (1)
[ARCHIVE_HEADER].archive as FirstArchive,  
[ARCHIVE_HEADER].cr_time,
[ARCHIVE_HEADER].group_name,
[VOLUME].vol_name,
[ARCHIVE_HEADER].display_name
FROM            
ARCHIVE_HEADER INNER JOIN VOLUME
ON ARCHIVE_HEADER.volume_key = VOLUME.volume_key
WHERE        
VOLUME.vol_name = @Volume
ORDER BY
ARCHIVE_HEADER.archive DESC

--now both records should be in #t
SELECT *
FROM #t
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...