Левое соединение с именем динамической таблицы, полученным из столбца

Я новичок в PostgreSQL, и мне интересно, можно ли использовать number из таблицы tbc как часть имени таблицы в левом объединении 'pa' || number. Так, например, если номер 456887, я хочу левое соединение с таблицей pa456887. Что-то вроде этого:

SELECT tdc.cpa, substring(tdc.ku,'[0-9]+') AS number, paTab.vym 
FROM public."table_data_C" AS tdc
LEFT JOIN concat('pa' || number) AS paTab ON (paTab.cpa = tdc.cpa)

И я хочу использовать только PostgreSQL, а не дополнительный код на PHP, например.


person Lubos K.    schedule 16.07.2014    source источник
comment
Это звучит как ужасная модель данных. Вместо этого вы должны исправить это.   -  person a_horse_with_no_name    schedule 16.07.2014
comment
@a_horse_with_no_name Я согласен, но что делать, если у вас есть такое требование?   -  person chrismarx    schedule 18.09.2019


Ответы (1)


В любом случае вам нужен динамический SQL.

Имя таблицы как заданный параметр

CREATE OR REPLACE FUNCTION foo(_number int)
  RETURNS TABLE (cpa int, nr text, vym text) AS  -- adapt to actual data types!
$func$
BEGIN
   RETURN QUERY EXECUTE format(
      'SELECT t.cpa, substring(t.ku,'[0-9]+'), p.vym 
       FROM   public."table_data_C" t
       LEFT   JOIN %s p USING (cpa)'
     , 'pa' || _number
     );
END
$func$ LANGUAGE plpgsql;

Вызов:

SELECT * FROM foo(456887)

Как правило, вы должны очищать имена таблиц с помощью format ( %I ), чтобы избежать внедрения SQL. Только с integer в качестве динамического ввода в этом нет необходимости. Дополнительные сведения и ссылки в этом связанном ответе:
INSERT с именем динамической таблицы в функции триггера

Модель данных

Для модели данных могут быть веские причины. Например, разделение/сегментирование или отдельные привилегии...
Если у вас нет такой веской причины, рассмотрите возможность объединения нескольких таблиц с одинаковой схемой в одну и добавьте столбец number as. Тогда вам не нужен динамический SQL.

Рассмотрим наследование. Затем вы можете добавить условие для tableoid, чтобы извлекать строки только из данной дочерней таблицы:

SELECT * FROM parent_table
WHERE  tableoid = 'pa456887'::regclass

Однако помните об ограничениях наследования. Связанные ответы:

Имя 2-й таблицы в зависимости от значения в 1-й таблице

Получение имени таблицы соединений из значений в первой таблице динамически усложняет ситуацию.

Всего за несколько столов

LEFT JOIN каждый по tableoid. В строке есть только одно совпадение, поэтому используйте COALESCE.

SELECT t.*, t.tbl, COALESCE(p1.vym, p2.vym, p3.vym) AS vym
FROM  (
   SELECT cpa, ('pa' || substring(ku,'[0-9]+'))::regclass AS tbl
   FROM   public."table_data_C"
   -- WHERE <some condition>
   ) t
LEFT   JOIN pa456887 p1 ON p1.cpa = t.cpa AND p1.tableoid = t.tbl
LEFT   JOIN pa456888 p2 ON p2.cpa = t.cpa AND p2.tableoid = t.tbl
LEFT   JOIN pa456889 p3 ON p3.cpa = t.cpa AND p3.tableoid = t.tbl

Для многих столов

Объедините цикл с динамическими запросами:

CREATE OR REPLACE FUNCTION foo(_number int)
  RETURNS TABLE (cpa int, nr text, vym text) AS
$func$
DECLARE
   _nr text;
BEGIN
FOR _nr IN
   SELECT DISTINCT substring(ku,'[0-9]+')
   FROM   public."table_data_C"
LOOP
   RETURN QUERY EXECUTE format(
      'SELECT t.cpa, _nr, p.vym 
       FROM   public."table_data_C" t
       LEFT   JOIN %I p USING (cpa)
       WHERE  t.ku LIKE (_nr || '%')'
     , 'pa' || _nr
     );
END LOOP;

END
$func$ LANGUAGE plpgsql;
person Erwin Brandstetter    schedule 16.07.2014
comment
Большое спасибо за ваш ответ. Но этот номер 456887, который я ввожу в функцию foo(), является номером из базы данных (это номер из первой выбранной подстроки (t.ku, '[0-9]+')). - person Lubos K.; 17.07.2014
comment
@LubosK.: Тогда это усложняет задачу. Я действительно думаю, что ваша модель данных должна быть улучшена. На данный момент: сколько строк вы извлекаете одновременно? И к скольким различным столам вы присоединяетесь? Может ли быть несколько различных значений в tdc.ku, начинающихся с одного и того же числа? - person Erwin Brandstetter; 17.07.2014
comment
@LubosK.: Я добавил два решения, которые должны охватывать все случаи. - person Erwin Brandstetter; 17.07.2014
comment
Здорово. Большое спасибо за ваш ответ, время и все - person Lubos K.; 17.07.2014