我想知道这在SQL中是否可行。假设你有两个表A和B,你在表A上做一个选择,在表B上做一个连接:
SELECT a.*, b.* FROM TABLE_A a JOIN TABLE_B b USING (some_id);
如果表A有“a_id”、“name”、“some_id”列,表B有“b_id”、“name”、“some_id”列,查询将返回“a_id”、“name”、“some_id”、“b_id”、“name”、“some_id”列。有什么方法可以为表B的列名加上前缀而不单独列出每一列吗?等价于这个:
SELECT a.*, b.b_id as 'b.b_id', b.name as 'b.name', b.some_id as 'b.some_id'
FROM TABLE_A a JOIN TABLE_B b USING (some_id);
但是,如前所述,没有列出每一列,所以像这样:
SELECT a.*, b.* as 'b.*'
FROM TABLE_A a JOIN TABLE_B b USING (some_id);
基本上就是说,“用‘something’为b.*返回的每一列添加前缀”。这可能吗,还是我运气不好?
编辑
关于不使用SELECT *等的建议是有效的建议,但与我的上下文无关,因此请关注眼前的问题——是否可以在连接中为表的所有列名添加前缀(SQL查询中指定的常量)?
我的最终目标是能够通过连接对两个表执行SELECT *操作,并且能够从结果集中获得的列的名称中分辨出哪些列来自表a,哪些列来自表b。同样,我不想单独列出列,我需要能够执行SELECT *操作。
最近在NodeJS和Postgres中遇到了这个问题。
ES6方法
我知道没有任何RDBMS特性提供这种功能,所以我创建了一个包含我所有字段的对象,例如:
const schema = { columns: ['id','another_column','yet_another_column'] }
定义了一个reducer将字符串与表名连接在一起:
const prefix = (table, columns) => columns.reduce((previous, column) => {
previous.push(table + '.' + column + ' AS ' + table + '_' + column);
return previous;
}, []);
这将返回一个字符串数组。为每个表调用它并合并结果:
const columns_joined = [...prefix('tab1',schema.columns), ...prefix('tab2',schema.columns)];
输出最后的SQL语句:
console.log('SELECT ' + columns_joined.join(',') + ' FROM tab1, tab2 WHERE tab1.id = tab2.id');
这个问题在实践中很有用。在软件编程中,只需要列出所有显式列,在这些列中,您需要特别小心地处理所有条件。
想象一下,当调试或尝试使用DBMS作为日常办公工具,而不是特定程序员的抽象底层基础设施的可变实现时,我们需要编写大量的sql。这种场景随处可见,比如数据库转换、迁移、管理等。这些sql大多只执行一次,不会再使用,给每个列名只是浪费时间。不要忘记SQL的发明不仅仅是为程序员使用的。
通常我会创建一个带列名前缀的实用程序视图,这里是pl/pgsql中的函数,这并不容易,但你可以将它转换为其他过程语言。
-- Create alias-view for specific table.
create or replace function mkaview(schema varchar, tab varchar, prefix varchar)
returns table(orig varchar, alias varchar) as $$
declare
qtab varchar;
qview varchar;
qcol varchar;
qacol varchar;
v record;
sql varchar;
len int;
begin
qtab := '"' || schema || '"."' || tab || '"';
qview := '"' || schema || '"."av' || prefix || tab || '"';
sql := 'create view ' || qview || ' as select';
for v in select * from information_schema.columns
where table_schema = schema and table_name = tab
loop
qcol := '"' || v.column_name || '"';
qacol := '"' || prefix || v.column_name || '"';
sql := sql || ' ' || qcol || ' as ' || qacol;
sql := sql || ', ';
return query select qcol::varchar, qacol::varchar;
end loop;
len := length(sql);
sql := left(sql, len - 2); -- trim the trailing ', '.
sql := sql || ' from ' || qtab;
raise info 'Execute SQL: %', sql;
execute sql;
end
$$ language plpgsql;
例子:
-- This will create a view "avp_person" with "p_" prefix to all column names.
select * from mkaview('public', 'person', 'p_');
select * from avp_person;
最近在NodeJS和Postgres中遇到了这个问题。
ES6方法
我知道没有任何RDBMS特性提供这种功能,所以我创建了一个包含我所有字段的对象,例如:
const schema = { columns: ['id','another_column','yet_another_column'] }
定义了一个reducer将字符串与表名连接在一起:
const prefix = (table, columns) => columns.reduce((previous, column) => {
previous.push(table + '.' + column + ' AS ' + table + '_' + column);
return previous;
}, []);
这将返回一个字符串数组。为每个表调用它并合并结果:
const columns_joined = [...prefix('tab1',schema.columns), ...prefix('tab2',schema.columns)];
输出最后的SQL语句:
console.log('SELECT ' + columns_joined.join(',') + ' FROM tab1, tab2 WHERE tab1.id = tab2.id');
PHP 7.2 + MySQL/Mariadb
MySQL会给你发送多个相同名称的字段。甚至在终端客户端。但如果你想要一个关联数组,你必须自己创建键。
感谢@axelbrz的原创。我已经将它移植到更新的php,并对它进行了一些清理:
function mysqli_rows_with_columns($link, $query) {
$result = mysqli_query($link, $query);
if (!$result) {
return mysqli_error($link);
}
$field_count = mysqli_num_fields($result);
$fields = array();
for ($i = 0; $i < $field_count; $i++) {
$field = mysqli_fetch_field_direct($result, $i);
$fields[] = $field->table . '.' . $field->name; # changed by AS
#$fields[] = $field->orgtable . '.' . $field->orgname; # actual table/field names
}
$rows = array();
while ($row = mysqli_fetch_row($result)) {
$new_row = array();
for ($i = 0; $i < $field_count; $i++) {
$new_row[$fields[$i]] = $row[$i];
}
$rows[] = $new_row;
}
mysqli_free_result($result);
return $rows;
}
$link = mysqli_connect('localhost', 'fixme', 'fixme', 'fixme');
print_r(mysqli_rows_with_columns($link, 'select foo.*, bar.* from foo, bar'));