我想知道这在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 *操作。
从这个解决方案出发,我将如何处理这个问题:
首先创建一个所有AS语句的列表:
DECLARE @asStatements varchar(8000)
SELECT @asStatements = ISNULL(@asStatements + ', ','') + QUOTENAME(table_name) + '.' + QUOTENAME(column_name) + ' AS ' + '[' + table_name + '.' + column_name + ']'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TABLE_A' OR TABLE_NAME = 'TABLE_B'
ORDER BY ORDINAL_POSITION
然后在你的查询中使用它:
EXEC('SELECT ' + @asStatements + ' FROM TABLE_A a JOIN TABLE_B b USING (some_id)');
然而,这可能需要修改,因为类似的东西只在SQL Server中测试。但是这段代码在SQL Server中并不完全有效,因为不支持USING。
请评论,如果你可以测试/纠正这段代码,例如MySQL。
对此没有SQL标准。
然而,通过代码生成(在表创建或修改或运行时按需生成),你可以很容易地做到这一点:
CREATE TABLE [dbo].[stackoverflow_329931_a](
[id] [int] IDENTITY(1,1) NOT NULL,
[col2] [nchar](10) NULL,
[col3] [nchar](10) NULL,
[col4] [nchar](10) NULL,
CONSTRAINT [PK_stackoverflow_329931_a] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[stackoverflow_329931_b](
[id] [int] IDENTITY(1,1) NOT NULL,
[col2] [nchar](10) NULL,
[col3] [nchar](10) NULL,
[col4] [nchar](10) NULL,
CONSTRAINT [PK_stackoverflow_329931_b] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
DECLARE @table1_name AS varchar(255)
DECLARE @table1_prefix AS varchar(255)
DECLARE @table2_name AS varchar(255)
DECLARE @table2_prefix AS varchar(255)
DECLARE @join_condition AS varchar(255)
SET @table1_name = 'stackoverflow_329931_a'
SET @table1_prefix = 'a_'
SET @table2_name = 'stackoverflow_329931_b'
SET @table2_prefix = 'b_'
SET @join_condition = 'a.[id] = b.[id]'
DECLARE @CRLF AS varchar(2)
SET @CRLF = CHAR(13) + CHAR(10)
DECLARE @a_columnlist AS varchar(MAX)
DECLARE @b_columnlist AS varchar(MAX)
DECLARE @sql AS varchar(MAX)
SELECT @a_columnlist = COALESCE(@a_columnlist + @CRLF + ',', '') + 'a.[' + COLUMN_NAME + '] AS [' + @table1_prefix + COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @table1_name
ORDER BY ORDINAL_POSITION
SELECT @b_columnlist = COALESCE(@b_columnlist + @CRLF + ',', '') + 'b.[' + COLUMN_NAME + '] AS [' + @table2_prefix + COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @table2_name
ORDER BY ORDINAL_POSITION
SET @sql = 'SELECT ' + @a_columnlist + '
,' + @b_columnlist + '
FROM [' + @table1_name + '] AS a
INNER JOIN [' + @table2_name + '] AS b
ON (' + @join_condition + ')'
PRINT @sql
-- EXEC (@sql)
我根据答案实现了一个解决方案,建议在节点中使用虚拟或哨兵列。你可以通过生成SQL来使用它:
select
s.*
, '' as _prefix__creator_
, u.*
, '' as _prefix__speaker_
, p.*
from statements s
left join users u on s.creator_user_id = u.user_id
left join persons p on s.speaker_person_id = p.person_id
然后对从数据库驱动程序返回的行进行后处理,比如addPrefixes(row)。
实现(基于我的驱动程序返回的字段/行,但应该很容易为其他DB驱动程序更改):
const PREFIX_INDICATOR = '_prefix__'
const STOP_PREFIX_INDICATOR = '_stop_prefix'
/** Adds a <prefix> to all properties that follow a property with the name: PREFIX_INDICATOR<prefix> */
function addPrefixes(fields, row) {
let prefix = null
for (const field of fields) {
const key = field.name
if (key.startsWith(PREFIX_INDICATOR)) {
if (row[key] !== '') {
throw new Error(`PREFIX_INDICATOR ${PREFIX_INDICATOR} must not appear with a value, but had value: ${row[key]}`)
}
prefix = key.substr(PREFIX_INDICATOR.length)
delete row[key]
} else if (key === STOP_PREFIX_INDICATOR) {
if (row[key] !== '') {
throw new Error(`STOP_PREFIX_INDICATOR ${STOP_PREFIX_INDICATOR} must not appear with a value, but had value: ${row[key]}`)
}
prefix = null
delete row[key]
} else if (prefix) {
const prefixedKey = prefix + key
row[prefixedKey] = row[key]
delete row[key]
}
}
return row
}
测试:
const {
addPrefixes,
PREFIX_INDICATOR,
STOP_PREFIX_INDICATOR,
} = require('./BaseDao')
describe('addPrefixes', () => {
test('adds prefixes', () => {
const fields = [
{name: 'id'},
{name: PREFIX_INDICATOR + 'my_prefix_'},
{name: 'foo'},
{name: STOP_PREFIX_INDICATOR},
{name: 'baz'},
]
const row = {
id: 1,
[PREFIX_INDICATOR + 'my_prefix_']: '',
foo: 'bar',
[STOP_PREFIX_INDICATOR]: '',
baz: 'spaz'
}
const expected = {
id: 1,
my_prefix_foo: 'bar',
baz: 'spaz',
}
expect(addPrefixes(fields, row)).toEqual(expected)
})
})