69

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

有没有办法做到这一点?

4

10 回答 10

46

你可以

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

那么这些列将至少按表排序返回。

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

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

(编辑以删除不必要的显式别名,请参阅评论。)

于 2012-10-31T07:51:11.773 回答
35

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

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;

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

于 2012-10-31T07:48:47.990 回答
15

我确信在连接中使用表名作为前缀和/或后缀字段名称的这种功能应该包含在ANSI SQL STANDARD中。目前,在 2019 年,仍然没有优雅的跨平台方式来做到这一点,剩下的就是使用别名的丑陋且容易出错的手动黑客,或涉及动态 sql 的特定于平台的解决方案。每个人都会真正受益于能够为“点星”(.*) 表示的字段指定自定义前缀或/和后缀。添加此类功能后的示例选择将是:

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

这样的特性将允许避免显式指定需要的完整(并且可能很大)字段列表的必要性,而不是仅指定星号和一些不需要的字段。

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

PS 又一个丑陋的,但至少是自动化和通用的动态 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 个表并希望避免从数据集表中获取大数据字段:

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 条件旨在仅检索没有真实数据的字段名称。

于 2019-09-07T10:43:15.653 回答
12

动态命名列的方法是生成一个引用 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;

如果需要,此命令将提高组连接限制。-

于 2013-06-06T15:35:40.793 回答
8

我最终只是为查询构建了字段集,截至 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声明中。

于 2020-03-24T16:39:03.923 回答
3

根据 koljaTM 和 AndriyM 提出的解决方案,也许更好的解决方案是这样编写您的查询:

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

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

如果 SQL 提供了一种自动为列名添加表名前缀的方法,那就更好了……

于 2014-03-26T10:49:42.240 回答
3

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

于 2012-10-31T08:15:37.603 回答
0

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

WHERE c.TABLE_SCHEMA='YOUR_SCHEMA_NAME' AND c.TABLE_NAME IN (....)
于 2017-09-15T10:03:19.917 回答
0
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 中的标准。

于 2021-12-07T06:11:46.603 回答
-1

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

于 2019-08-31T08:06:51.137 回答