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