Как мне объединить 2 оператора select в одно? - PullRequest
43 голосов
/ 12 февраля 2009

Я нуб, когда дело доходит до синтаксиса SQL.

У меня есть таблица с множеством строк и столбцов, конечно: P Допустим, это выглядит так:

      AAA BBB CCC DDD
-----------------------
Row1 | 1   A   D   X
Row2 | 2   B   C   X
Row3 | 3   C   D   Z

Теперь я хочу создать расширенный оператор выбора, который дает мне это вместе (псевдо SQLish здесь):

select 'Test1', * from TABLE Where CCC='D' AND DDD='X'
select 'Test2', * from TABLE Where CCC<>'D' AND DDD='X'

Вывод будет:

Test1, 1, A, D, X
Test2, 2, B, C, X

Как бы я объединил эти два оператора select в один красивый оператор select?

Будет ли это работать, если я усложню SQL, как показано ниже (потому что мой собственный оператор SQL содержит оператор существующие)? Я просто хочу знать, как я могу объединить селекторы, а затем попытаться применить их к моему более продвинутому SQL.

select 'Test1', * from TABLE Where CCC='D' AND DDD='X' AND exists(select ...)
select 'Test2', * from TABLE Where CCC<>'D' AND DDD='X' AND exists(select ...)




Вот мой РЕАЛЬНЫЙ SQL-оператор:

select Status, * from WorkItems t1
where  exists (select 1 from workitems t2 where t1.TextField01=t2.TextField01 AND (BoolField05=1) )
AND TimeStamp=(select max(t2.TimeStamp) from workitems t2 where t2.TextField01=t1.TextField01) 
AND TimeStamp>'2009-02-12 18:00:00'

что дает мне результат. Но я хочу объединить его с копией этого оператора select с добавленным AND в конце, и поле «Status» будет изменено строкой, такой как «DELETED».

select 'DELETED', * from WorkItems t1
where  exists (select 1 from workitems t2 where t1.TextField01=t2.TextField01 AND (BoolField05=1) )
AND TimeStamp=(select max(t2.TimeStamp) from workitems t2 where t2.TextField01=t1.TextField01) 
AND TimeStamp>'2009-02-12 18:00:00'
AND NOT (BoolField05=1)

Ответы [ 8 ]

60 голосов
/ 12 февраля 2009

У вас есть два варианта здесь. Первый - иметь два набора результатов, которые будут устанавливать «Test1» или «Test2» на основе условия в предложении WHERE, а затем UNION их вместе:

select 
    'Test1', * 
from 
    TABLE 
Where 
    CCC='D' AND DDD='X' AND exists(select ...)
UNION
select 
    'Test2', * 
from 
    TABLE
Where
    CCC<>'D' AND DDD='X' AND exists(select ...)

Это может быть проблемой, потому что вы собираетесь эффективно сканировать / искать дважды по TABLE.

Другим решением было бы выбрать из таблицы один раз и установить «Test1» или «Test2» в зависимости от условий в TABLE:

select 
    case 
        when CCC='D' AND DDD='X' AND exists(select ...) then 'Test1'
        when CCC<>'D' AND DDD='X' AND exists(select ...) then 'Test2'
    end,
    * 
from 
    TABLE 
Where 
    (CCC='D' AND DDD='X' AND exists(select ...)) or
    (CCC<>'D' AND DDD='X' AND exists(select ...))

Смысл в том, что вам придется дублировать условия фильтрации в операторе CASE и в операторе WHERE.

9 голосов
/ 12 февраля 2009

Если они из одной таблицы, я думаю UNION - это команда, которую вы ищете.

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

3 голосов
/ 13 февраля 2009

Спасибо за ввод. Пробовал материал, который был упомянут здесь, и вот те 2, которые я получил на работу:

(
select 'OK', * from WorkItems t1
where exists(select 1 from workitems t2 where t1.TextField01=t2.TextField01 AND (BoolField05=1) )
AND TimeStamp=(select max(t2.TimeStamp) from workitems t2 where t2.TextField01=t1.TextField01) 
AND TimeStamp>'2009-02-12 18:00:00'
AND (BoolField05=1)
)
UNION
(
select 'DEL', * from WorkItems t1
where exists(select 1 from workitems t2 where t1.TextField01=t2.TextField01 AND (BoolField05=1) )
AND TimeStamp=(select max(t2.TimeStamp) from workitems t2 where t2.TextField01=t1.TextField01) 
AND TimeStamp>'2009-02-12 18:00:00'
AND NOT (BoolField05=1)
)

И

select 
    case
        when
            (BoolField05=1)
    then 'OK'
    else 'DEL'
        end,
        *
from WorkItems t1
Where
            exists(select 1 from workitems t2 where t1.TextField01=t2.TextField01 AND (BoolField05=1) )
            AND TimeStamp=(select max(t2.TimeStamp) from workitems t2 where t2.TextField01=t1.TextField01) 
            AND TimeStamp>'2009-02-12 18:00:00'

Что было бы наиболее эффективным из них (редактировать: второе, поскольку оно сканирует таблицу только один раз), и возможно ли сделать его еще более эффективным ? (BoolField = 1) действительно переменная (dyn sql), которая может содержать любой оператор where в таблице.

Я работаю на MS SQL 2005. Пробовал Quassnoi примеры, но не работал должным образом.

1 голос
/ 12 февраля 2009

Я думаю, это то, что вы ищете:

SELECT CASE WHEN BoolField05 = 1 THEN Status ELSE 'DELETED' END AS MyStatus, t1.*
FROM WorkItems t1
WHERE (TextField01, TimeStamp) IN(
  SELECT TextField01, MAX(TimeStamp)
  FROM WorkItems t2
  GROUP BY t2.TextField01
  )
AND TimeStamp > '2009-02-12 18:00:00'

Если вы в Oracle или в MS SQL 2005 и выше, вы можете сделать:

SELECT *
FROM (
  SELECT CASE WHEN BoolField05 = 1 THEN Status ELSE 'DELETED' END AS MyStatus, t1.*,
     ROW_NUMBER() OVER (PARTITION BY TextField01 ORDER BY TimeStamp DESC) AS rn
  FROM WorkItems t1
) to
WHERE rn = 1

, это более эффективно.

1 голос
/ 12 февраля 2009

используйте регистр для выбора и используйте где закрыть ИЛИ

как-то так, я не проверял, но это должно работать, я думаю ...

select case when CCC='D' then 'test1' else 'test2' end, *
from table
where (CCC='D' AND DDD='X') or (CCC<>'D' AND DDD='X')
1 голос
/ 12 февраля 2009

Команда Union - это то, что вам нужно. Если это не сработает, вам, возможно, придется уточнить, в какой среде вы находитесь.

1 голос
/ 12 февраля 2009
select Status, * from WorkItems t1
where  exists (select 1 from workitems t2 where t1.TextField01=t2.TextField01 AND (BoolField05=1) )
AND TimeStamp=(select max(t2.TimeStamp) from workitems t2 where t2.TextField01=t1.TextField01) 
AND TimeStamp>'2009-02-12 18:00:00'

UNION

select 'DELETED', * from WorkItems t1
where  exists (select 1 from workitems t2 where t1.TextField01=t2.TextField01 AND (BoolField05=1) )
AND TimeStamp=(select max(t2.TimeStamp) from workitems t2 where t2.TextField01=t1.TextField01) 
AND TimeStamp>'2009-02-12 18:00:00'
AND NOT (BoolField05=1)

Возможно, это бы сработало. Я не могу проверить это отсюда, и я не уверен, с какой версией SQL вы работаете.

0 голосов
/ 03 июля 2017
select t1.* from 
(select * from TABLE Where CCC='D' AND DDD='X') as t1,
(select * from TABLE Where CCC<>'D' AND DDD='X') as t2

Еще один способ сделать это!

...