Поиск по нескольким таблицам, а также отображение имени таблицы в результирующих строках

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

Сценарий таков, что у меня есть несколько таблиц с одинаковыми именами столбцов в каждой. Это данные, которые я получил от сторонних организаций, и храню их в разных таблицах.

Те же таблицы выглядят так:

Table 1: pid, parent_name, student_name, student_number, class_name, columnN
Table 2: pid, previous_school, previous_school, student_number, columnN
Table 3: pid, student_name, student_number, parent_name, column4, columnN
Table 14: pid, student_number, parent_name, column4, columnN
Table N: pid, previous_school, parent_name, column4, columnN

Мне нужен оператор SQL, который ищет student_name по всем таблицам в псевдокоде: for each table, find a student named john doe and return to me the row where you got the result and the table where you found the result

Дайте результат в следующей презентации:

john doe, Table 1, pid
john doe, Table 9, pid

Чтобы сделать это немного сложнее, столбец student_name может быть не во всех таблицах, поэтому запрос должен выполняться любезно, если столбец там не найден.

3 ответа

Решение

Вы ищете динамический SQL. Соберите ваш запрос из системного каталога автоматически:

SELECT string_agg('SELECT student_name, '''
                   || c.oid::regclass || ''' AS tbl, pid FROM '
                   || c.oid::regclass
                   || $$ WHERE student_name = 'John Doe'$$
                 , E'\nUNION ALL\n')
FROM   pg_namespace n
JOIN   pg_class     c ON c.relnamespace = n.oid
WHERE  n.nspname = 'public'         -- schema name where your tables lie
AND    c.relname LIKE 't%'          -- and / or filter table names
AND    EXISTS (
   SELECT 1 FROM pg_attribute 
   WHERE  attrelid = c.oid
   AND    attname = 'student_name'  -- make sure column exists
   AND    NOT attisdropped          -- and is alive
   );

Создает строку запроса:

SELECT student_name, 'tbl1' AS tbl, pid FROM tbl1 WHERE student_name = 'John Doe'
UNION ALL
SELECT student_name, 'tbl2' AS tbl, pid FROM tbl2 WHERE student_name = 'John Doe'
UNION ALL
SELECT student_name, 'tbl3' AS tbl, pid FROM tbl3 WHERE student_name = 'John Doe'
...

Затем запустите его во втором вызове или полностью автоматизируйте с помощью функции PL/pgSQL, используя EXECUTE, Пример:
Выберите динамический набор столбцов из таблицы и получите сумму для каждого

Этот запрос создает безопасный код с очищенными идентификаторами, предотвращающими внедрение SQL. (Объяснение для oid::regclass Вот.)

Есть больше связанных ответов. Используйте поиск.

КСТАТИ, LIKE в student_name LIKE 'John Doe' бессмысленно. Без подстановочных знаков, просто используйте =,

Просто добавьте буквальное значение для каждого выбора, который описывает исходную таблицу:

select student_name, 'Table 1', pid
from table1
where ...
union
select some_name, 'Table 2', pid
from table2
where ...
union
...

Вы можете получить имена таблиц из представления all_tables, динамически создать запрос на объединение, а затем выполнить его с помощью команды немедленного выполнения. Примерно так: будьте осторожны, код может содержать ошибки:

DECLARE
  v_query VARCHAR2(4000) := '';
  v_student_name VARCHAR2(50) := 'John Doe';

  type r_results is record (
    student_name VARCHAR2(500),
    table_name VARCHAR2(100),
    pid NUMBER
  );

  v_results r_results;

  CURSOR c_tables IS
    SELECT table_name 
    FROM all_tables 
    WHERE upper(table_name) LIKE '%_STUDENT_RECORDS';
BEGIN      
  FOR client_table IN c_tables LOOP
    IF v_query IS NOT NULL THEN
      v_query := v_query || ' UNION ';
    END IF;

    v_query := v_query || 'SELECT student_name, ' || client_table.table_name || ', ' || pid FROM ' || client_table.table_name || ' WHERE student_name = ''' || v_student_name || '''';
  END LOOP;


  EXECUTE IMMEDIATE v_query INTO v_results;
END;
Другие вопросы по тегам