2015-07-15 3 views
0

У меня есть две таблицы с одинаковыми столбцами в одинаковом порядке. У меня есть желание присоединиться к одной из двух таблиц, в зависимости от условия подзапроса. Например, предположим, что у меня есть следующая схема:Postgres Выберите из таблицы на основе результата запроса

CREATE TABLE b (
    bid SERIAL PRIMARY KEY, 
    cid INT NOT NULL 
); 

CREATE TABLE a1 (
    aid SERIAL PRIMARY KEY, 
    bid INT NOT NULL REFERENCES b 
); 

CREATE TABLE a2 (
    aid SERIAL PRIMARY KEY, 
    bid INT NOT NULL REFERENCES b 
); 

Я хотел бы запрос, который выполняет соединение через любой a1 или a2 на основе некоторого условия. Что-то вроде:

WITH z AS (
    SELECT cid, someCondition FROM someTable 
) 
SELECT * 
FROM CASE z.someCondition THEN a1 ELSE a2 END 
JOIN b USING (bid) 
WHERE cid = (SELECT cid FROM z); 

Однако вышеуказанное не работает. Есть ли способ условного соединения через a1 или a2, в зависимости от некоторого булевского условия, хранящегося в таблице z?

ответ

2

Если условия являются исключительными (я ожидаю, что они есть): вобще как запросы и UNION ALL их, с смарт-накидной конструкции:

WITH z AS (
    SELECT cid 
     , (cid %3) AS some_condition -- Fake ... 
     FROM b 
) 
SELECT * 
    FROM a1 
    JOIN b USING (bid) 
WHERE EXISTS(SELECT * FROM z 
     WHERE some_condition = 1 AND cid = b.cid) 
UNION ALL 
SELECT * 
    FROM a2 
    JOIN b USING (bid) 
WHERE EXISTS(SELECT * FROM z 
     WHERE some_condition = 2 AND cid = b.cid) 
     ; 

несколько другой синтаксис, чтобы сделать то же самое:

WITH z AS (
    SELECT cid 
     , (cid %3) AS some_condition 
     FROM b 
) 
SELECT * 
    FROM a1 
    JOIN b ON a1.bid = b.bid 
     AND EXISTS(SELECT * FROM z 
     WHERE some_condition = 1 AND cid = b.cid) 
UNION ALL 
SELECT * 
    FROM a2 
    JOIN b ON a2.bid = b.bid 
     AND EXISTS(SELECT * FROM z 
     WHERE some_condition = 2 AND cid = b.cid) 
     ; 
+0

Это интересная идея. По правде говоря, таблица * a1 * является надмножеством таблицы * a2 *, и реальный вопрос, который я задаю, заключается в следующем: 'IF EXISTS in * a2 * OR (условие AND EXISTS in * a1 *)', что похоже на отлично подходит для вашего предложения. Я могу даже переместить * UNION ALL * в другой подзапрос, поэтому я могу выполнить соединение только один раз. –

+0

Выполнение JOIN только один раз - это, как правило, плохая идея: в моем примере два JOIN могут делать индексы. После вашего UNION индексы не будут использоваться. (BTW: joins дешевы, это не 1985 год) Примечание: в SQL нет 'IF'. Вам это не нужно: просто переместите условие в предложение WHERE. – wildplasser

+0

Я не думаю, что аргумент индексов имеет значение для меня ... в реальных таблицах должно быть либо ноль, либо одно совпадение из таблицы * a1 * или * a2 *, и я беру этот один результат и соединяю цепочку из 4 таблиц. Я не хочу дублировать одну и ту же логику несколько раз. ПРИМЕР: 'С z AS (...), y AS (SELECT bid FROM a1 WHERE aid = 7 AND some_condition UNION ALL SELECT bid FROM a2 WHERE aid = 7 AND NOT some_condition) SELECT COUNT (1) :: INT :: BOOLEAN FROM b WHERE bid = (SELECT * FROM y) И cid = (SELECT cid FROM z) ' –

2

Синтаксис SQL не позволяет условные объединения. Наверное, самый простой способ достижения подобного эффекта является использование динамического запроса в функции plpgsql, которая может выглядеть следующим образом:

create function conditional_select(acid int, some_condition boolean) 
returns table (aid int, bid int, cid int) 
language plpgsql as $$ 
declare 
    tname text; 
begin 
    if some_condition then tname = 'a1'; 
    else tname = 'a2'; 
    end if; 

    return query execute format ($fmt$ 
     select a.aid, b.bid, b.cid 
     from %s a 
     join b using(bid) 
     where cid = %s; 
     $fmt$, tname, acid); 
end $$; 


select * from conditional_select(1, true) 
+0

Я лично предпочитаю ответ @ wildplasser, просто потому, что он не требует настраиваемой базы данных ase. Однако это по-прежнему отличный подход. –

1

Если, как в вашем примере, у вас есть только несколько столбцов, которые вы хотите для вывода, вы можете использовать CASE заявление для каждого столбца:

SELECT CASE z.someCondition THEN a1.aid ELSE a2.aid END AS aid, 
     CASE z.someCondition THEN a1.bid ELSE a2.bid END AS bid 
FROM b 
JOIN a1 ON a1.bid = b.bid 
JOIN a2 ON a2.bid = b.bid 
JOIN someTable z USING (cid); 

в зависимости от размера таблиц a1 и a2 и сколько столбцов вы должны вывести, это может быть или мое не может быть быстрее, чем решение Клина с функция, которая по своей сути медленнее, чем простой SQL и канун Кроме того, из-за динамического запроса. Учитывая, что z.someCondition уже есть значение boolean, оценка CASE будет очень быстрой. Маленькие таблицы + несколько столбцов = это решение; большие таблицы + много столбцов = решение Клина.

Смежные вопросы