在连接中,如何使用它来自的表为所有列名添加前缀

时间:2012-10-31 07:42:54

标签: mysql sql

我正在分析一个相当可怕的遗留数据库/代码库,尝试通过将查询组合到联接中来减少服务器负载(包括通常调用超过一百万个单独查询的电子邮件警报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 | ...

有没有办法实现这个目标?

9 个答案:

答案 0 :(得分:33)

您可以在查询中命名字段并为其指定别名:

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;

...和一个好的文本编辑器和副本&糊。

答案 1 :(得分:27)

你可以

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

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

为了更好地区分每两组列,您还可以添加“delimiter”列,如下所示:

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

(已编辑为删除显式别名,请参阅注释。)

答案 2 :(得分:10)

动态命名列的方法是生成引用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限制。 -

答案 3 :(得分:3)

我在这个问题MySQL concat() to create column names to be used in a query?中发现了一些有用的东西。我认为这可能是解决方案之一。

答案 4 :(得分:3)

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

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

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

如果SQL提供了一种方法来自动为列名添加表名...

,那就更好了

答案 5 :(得分:3)

我最终只是为查询构建字段集,截至2020年仍不支持。

但是,作为一个懒惰的程序员,我显然不想为查询中的所有表手动输入所有内容。所以我写了一个查询来构建选择语句:

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语句中。

答案 6 :(得分:1)

我坚信,在联接中应该在表名中加上这样的前缀和/或后缀字段名,即 ANSI SQL STANDARD 。目前,在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

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

还有什么要添加到标准中的功能,就是能够从“加星标”(*)输出中排除某些字段,这是选择所有字段的快捷方式。我会添加 except 关键字以列出由于减少网络数据传输或/和简洁性等原因而不想包含的文件。 :

select * except large_binary_data_field,another_notneeded_field,etc from my_table

与仅指定星号和一些不需要的字段相反,该功能将避免需要明确指定需要的完整(并且可能很大)字段列表的必要性/ em>。

因此,请阅读本文并能够联系ANSI SQL标准影响者的人,知道该怎么做)

P.S。 另一个丑陋的地方,但至少是自动化的通用动态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个表,并希望避免从 数据集 <中获取大的 data 字段/ strong>表:

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 

其中***表示大量其他有用字段,其中某些字段对于一个以上的表是通用的(因此需要加前缀)。 cur 显然是psycopg游标,0 = 1条件用于仅检索没有真实数据的字段名称。

答案 7 :(得分:0)

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

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

答案 8 :(得分:0)

您可以尝试动态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