【问题标题】:Search across multiple tables and also display table name in resulting rows搜索多个表并在结果行中显示表名
【发布时间】:2014-04-23 21:19:54
【问题描述】:

如何构造一条 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

我需要一个在所有表中搜索 student_name 的 SQL 语句 在伪代码中: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 列可能不在所有表中,因此如果在那里没有找到该列,查询需要继续进行。

【问题讨论】:

标签: sql postgresql database-design dynamic-sql


【解决方案1】:

您正在寻找动态 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'
...

然后在第二次调用中运行它,或者使用EXECUTE 使用 PL/pgSQL 函数完全自动化它。示例:
Select a dynamic set of columns from a table and get the sum for each

此查询产生safe code with sanitized identifiers preventing SQL injection.(此处为oid::regclass 的解释。)

还有更多相关的答案。 Use a search.

顺便说一句,student_name LIKE 'John Doe' 中的LIKE 毫无意义。没有通配符,只需使用=

【讨论】:

  • 它确实给了我你指出的输出,但请原谅我问,我在哪里插入查询where student_name LIKE John Doe
  • @lukik:添加了 WHERE 子句。
【解决方案2】:

只需为每个描述源表的选择添加一个文字值:

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

【讨论】:

  • 不想一一指定表名。或者至少,给我一个选项来指定它可以搜索的表的list。目前这 30 个平面表都以 codeclient_number_student_records 开头,其中 client_number 为 1、2、3 等
【解决方案3】:

您可以从 all_tables 视图中获取表的名称,动态地创建联合查询,然后使用 execute immediate 执行它。像这样的东西——小心代码可能有错误——:

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;

【讨论】:

  • 出现错误:ERROR: syntax error at or near "VARCHAR2" LINE 2: v_query VARCHAR2(4000) := '';
  • 我编辑了代码。有一个未注释的注释行。大概就是这样。