在联接中,如何在所有列名前面加上它来自的表的前缀

我正在分析一个相当糟糕的遗留数据库/代码库,试图通过将查询合并到连接(包括通常调用超过100万个单独查询的电子邮件警报 cron 作业)来减少服务器负载。

SELECT * FROM
class_alerts_holding ah
INNER JOIN class_listings l ON l.id = ah.lid
INNER JOIN class_users u ON u.id = ah.uid
LEFT JOIN class_prodimages pi ON pi.pid = ah.lid

这里有120根柱子。

aid | id | lid | uid | oid | catName | searchtext | alertfreq | listType | id | owner | title | section | shortDescription | description | featured | price | display | hitcount | dateadded | expiration | url | notified | searchcount | repliedcount | pBold | pHighlighted | notes | ...

为了帮助我分析如何构造新的查询,如果我能在结果中的列前加上它们来自 JOIN 中的表的前缀,那就太棒了。

class_alerts_holding.aid | class_alerts_holding.id | class_listings.lid | ...

有办法做到吗?

79924 次浏览

您可以在查询中命名字段并给它们起别名:

SELECT     ah.whateverfield1 AS 'ah_field1',
ah.whateverfield2 AS 'ah_field2',
l.whateverfield3 AS 'l.field3',
[....]
FROM       class_alerts_holding ah
INNER JOIN class_listings l ON l.id = ah.lid
INNER JOIN class_users u ON u.id = ah.uid
LEFT JOIN  class_prodimages pi ON pi.pid = ah.lid

如果您有那么多字段,手动设置需要一些工作,但是您可以通过这个查询简化..。

SHOW FULL FIELDS FROM your_table_name;

... 和一个好的文本编辑器和复制粘贴。

你可以的

select ah.*, l.*, u.*, pi.* from ...

然后至少按表的顺序返回列。

为了更好地区分每两组列,您还可以像下面这样添加“分隔符”列:

select ah.*, ':', l.*, ':', u.*, ':', pi.* from ...

(编辑后可删除不必要的明确别名,请参阅注释。)

我在这个问题 创建要在查询中使用的列名称?中发现了一些有用的东西。我认为这可能是解决方案之一。

动态命名列的方法是生成引用 information _ schema 的预处理语句。这会给你你想要的结果。

SET @sql = NULL;
SELECT CONCAT(
'SELECT ',GROUP_CONCAT(c.TABLE_NAME,'.',c.COLUMN_NAME,' AS `',c.TABLE_NAME,'.',c.COLUMN_NAME,'`'),'
FROM class_alerts_holding
INNER JOIN class_listings ON class_listings.id = class_alerts_holding.lid
INNER JOIN class_users ON class_users.id = class_alerts_holding.uid
LEFT JOIN class_prodimages ON class_prodimages.pid = class_alerts_holding.lid'
)
INTO @sql
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_NAME IN ('class_alerts_holding','class_listings',
'class_users','class_prodimages');
PREPARE sql_statement FROM @sql;
EXECUTE sql_statement;

GROUP _ CONCAT ()函数的默认限制是1024个字符,因此根据表中列的数量,可能需要提高此限制以生成准备好的语句。

SET SESSION group_concat_max_len = 1000000;

如果需要,这个命令将提高组 concat 限制。-

基于 koljaTM 和 AndriyM 提出的解决方案,也许更好的解决方案是这样写查询:

select
'--TABLE_AAA:--', TABLE_AAA.*,
'--TABLE_BBB:--', TABLE_BBB.*,
'--TABLE_CCC:--', TABLE_CCC.*,
'--TABLE_DDD:--', TABLE_DDD.*
from ...

不幸的是,当一个(或多个)表包含的列名超过屏幕宽度时,这仍然不够好。(因此,您可能在屏幕上看到20列,但仍然看不到它们来自的表的名称。)

如果 SQL 提供了一种方法,可以自动在列名前面加上表名,那么情况会更好... ..。

@ alden-w,您可以将 TABLE _ SCHEMA 条件添加到不要混淆来自不同模式的相同表名称的位置

WHERE c.TABLE_SCHEMA='YOUR_SCHEMA_NAME' AND c.TABLE_NAME IN (....)

您可以尝试使用动态 sql 根据表定义创建一个查询。

declare @col varchar(max)
set @col = Select stuff(
(select ', ' + column_name + '.' + table_name
from information_schema.columns
where table_name in ( 'table1', 'table2' ...) for xml
path('')),1,1,'')


declare @query nvarchar(max) = '
select ' + @col + '
from table1
inner join table2 on table1.id = table2.id '


exec sp_executesql @query


我相信,这样的功能,前缀和/或后缀字段名称与一个表名在联接应该包括到 ANSI SQL 标准。目前,在2019年,仍然没有一种优雅的跨平台方式来做到这一点,剩下的就是外观丑陋且容易出错的别名手动黑客攻击,或者涉及动态 sql 的平台特定解决方案。每个人都会从为‘ dot-star’(表示的字段指定自定义前缀或/和后缀的能力中获益。*).在添加这些功能后,样本选择如下:

select a.* use prefix,b.* use postfix '_b' from table_a a inner join table_b b on a.id=b.id

如您所见,默认情况下,前缀或后缀将等于表名(或别名名称) ,并且可以用任何所需的字符串文字重写。

另外,标准中需要加入的是从“ starred”(*)输出中排除某些字段的能力,这是选择所有字段的快捷方式。为了减少网络数据传输或/和简洁性,我会在列表中添加 除非关键字,例如:

select * except large_binary_data_field,another_notneeded_field,etc from my_table

这样的特性可以避免显式地指定完整(甚至可能很大)的字段列表,这些字段是 需要,而不是仅仅指定星号和一些字段是 不需要

所以,无论谁读了这篇文章,并且能够接触到 ANSI SQL 标准的影响者,你知道该怎么做)

又是一个丑陋的,但至少是自动的和通用的动态 sql 包装器

对于使用 Psycopg 的 Python 支持者来说,下面是我使用的方便的子代码(严格在内部使用,因为它容易受到 sql 注入的影响)

def get_table_fields(table,alias,prefix='',suffix='',excluding=''):
if type(excluding)==str: excluding=excluding.split(',')
cur.execute('select * from '+table+' where 0=1');cur.fetchall()
if not (cur.description is None):
return ','.join([alias+'.'+col.name+' '+prefix+col.name+suffix for col in cur.description if not (col.name in excluding)])

以及调用代码,其中我将连接3个表,并且希望避免从 < em > 数据集 表获取大的 环境监察及审核资料字段:

sql="""select %s,%s,%s from tasks t,features_sets f,datasets d
where
t.is_active=true and f.is_active=true
and f.task=t.id and t.train_dataset=d.id
""" % (
get_table_fields('tasks','t',prefix='ts_'),
get_table_fields('features_sets','f',prefix='fs_'),
get_table_fields('datasets','d',prefix='ds_',excluding='data')
)

它为我展开成强大的力量

select t.id ts_id,t.project ts_project,t.name ts_name,***,
fs_id,f.task fs_task,f.name fs_name,f.description fs_description,***,
d.id ds_id,d.project ds_project,d.name ds_name,***
from tasks t,features_sets f,datasets d
where
t.is_active=true and f.is_active=true
and f.task=t.id and t.train_dataset=d.id

在 * * * 表示成吨的其他有用字段的地方,其中一些字段对于不止一个表是常见的(因此需要前缀)。杂种显然是 Psycopg 游标,0 = 1条件的目的是只检索没有实际数据的字段名。

我最后只是为查询构建字段集,到2020年为止,这仍然不受支持。

但是,作为一个懒惰的程序员,我显然不想为我的查询中的所有表手动输入这一切。因此,我编写了一个查询来构建 select 语句:

SELECT
CONCAT(table_name, ".", column_name, " AS ", CHAR(34), table_name, ".", column_name, CHAR(34)) field_names
FROM
information_schema.columns
WHERE
table_schema = "my_database"
AND table_name IN(
"table_1",
"table_2"
);

它会输出类似于:

| field_names                        |
|------------------------------------|
| table_1.id AS "table_1.id"         |
| table_1.name AS "table_1.name"     |
| table_2.id AS "table_2.id"         |
| table_2.number AS "table_2.number" |

这样就可以很容易地复制到 SELECT语句中。

CREATE OR REPLACE FUNCTION getAlias (mytable text, my_alias text, my_prefix text)
RETURNS SETOF TEXT AS $$
SELECT my_alias || column_name || ' as ' || my_prefix
FROM information_schema.COLUMNS
WHERE TABLE_NAME = mytable;
$$ LANGUAGE SQL

——可以用 db 编写函数。这个函数需要成为 SQL 中的标准函数。

这对我来说工程下面的暗罗伯建议在 MS SQL。如果两个表共享某些列名,则以这种方式执行此操作可以避免出现“ Ambigous column name...”错误消息。

DECLARE @cols1 NVARCHAR(max)
SET @cols1 =  (SELECT STUFF(
(SELECT ', ati.' + [COLUMN_NAME] + ' AS ' + 'ati_' + [COLUMN_NAME] FROM information_schema.columns
WHERE [TABLE_NAME] in ('audit_trans_inv') FOR XML PATH('')),1,1,''))


DECLARE @cols2 NVARCHAR(max)
SET @cols2 =  (SELECT STUFF(
(SELECT ', ti.' + [COLUMN_NAME] + ' AS ' + 'ti_' + [COLUMN_NAME] from information_schema.columns
WHERE [TABLE_NAME] in ('transaccion_inv') FOR XML PATH('')),1,1,''))


DECLARE @sql NVARCHAR(max) = '
SELECT TOP 5 ' + @cols1 + ',' + @cols2 + '
FROM [millennium].[AUDIT_TRANS_INV] ati
INNER JOIN [millennium].[TRANSACCION_INV] ti ON [ti].[AUDIT_TRANS_INV] = [ati].[AUDIT_TRANS_INV]


EXEC sp_executesql @sql