使用子查询定义列别名
Using Subqueries to Define Column Alias
我有两个 table 为清楚起见,我在下面进行了简化。一个存储数据值,而另一个定义数据的单位和类型。有些测试有一个结果,有些可能有更多(我的实际 table 有结果 1-10):
Table 'Tests':
ID Result1 Result2 TestType(FK to TestTypes Type)
---------- ------------ ----------- -----------
1001 50 29 1
1002 90.9 NULL 2
1003 12.4 NULL 2
1004 20.2 30 1
Table 'TestTypes':
Type TestName Result1Name Result1Unit Result2Name Result2Unit ..........
------- --------- ------------ ----------- ------------ -----------
1 Temp Calib. Temperature F Variance %
2 Clarity Turbidity CU NULL NULL
我想在连接两个 table 时使用 ResultXName 作为列别名。换句话说,如果用户想要查看所有类型 1 'Temp Calib' 测试,则数据格式如下:
Temperature Variance
------------ -----------
50 F 10.1%
20.2 F 4.4%
或者如果他们查看类型 2,它只使用 1 个结果并且应该忽略 NULL:
Turbidity
----------
90.9 CU
12.4 CU
我在合并 table 的两列方面取得了一些成功:
SELECT CONCAT(Result1, ' ', ISNULL(Result1Unit, ''))
FROM Tests
INNER JOIN TestTypes ON Tests.TestType = TestTypes.Type
但我不知道如何使用 TestName 作为新的列别名。这是我一直在尝试使用子查询,但似乎子查询在 AS 子句中是不允许的:
SELECT CONCAT(Result1, ' ', ISNULL(Result1Unit, '')) AS (SELECT TOP(1) Result1Name FROM TestTypes WHERE Type = 1)
FROM Tests
INNER JOIN TestTypes ON Tests.TestType = TestTypes.Type
我可以使用其他方法吗?或者我是否需要重组我的数据来实现这一目标?我正在使用 MSSQL。
是的,这可以通过仔细构建动态 SQL 字符串完全自动化。本方案关键点及参考文献如下。
- Count the Result variables(第 1 节)
- 通过 using
sp_executesql
with the output
definition 获取 ResultXName
的新列名称(第 2-1 节)
- 附加新列的子句(第 2-2 节)
N.B.1。尽管动态 table 架构通常被认为是 糟糕的设计 ,但有时人们只是被命令这样做。因此,我不质疑此要求的充分性。
N.B.2。注意任意字符串执行的安全问题。根据您的用例,可能需要额外的字符串过滤器。
测试数据集
use [testdb];
GO
if OBJECT_ID('testdb..Tests') is not null
drop table testdb..Tests;
create table [Tests] (
[ID] int,
Result1 float,
Result2 float,
TestType int
)
insert into [Tests]([ID], Result1, Result2, TestType)
values (1001,50,29,1),
(1002,90.9,NULL,2),
(1003,12.4,NULL,2),
(1004,20.2,30,1);
if OBJECT_ID('testdb..TestTypes') is not null
drop table testdb..TestTypes;
create table [TestTypes] (
[Type] int,
TestName varchar(50),
Result1Name varchar(50),
Result1Unit varchar(50),
Result2Name varchar(50),
Result2Unit varchar(50)
)
insert into [TestTypes]([Type], TestName, Result1Name, Result1Unit, Result2Name, Result2Unit)
values (1,'Temp Calib.','Temperature','F','Variance','%'),
(2,'Clarity','Turbidity','CU',NULL,NULL);
--select * from [Tests];
--select * from [TestTypes];
解决方案
/* Input Parameter */
declare @type_no int = 1;
/* 1. determine the number of Results */
declare @n int;
-- If there are hundreds of results please use the method as of (2-1)
select @n = LEN(COALESCE(LEFT(Result1Name,1),''))
+ LEN(COALESCE(LEFT(Result2Name,1),''))
FROM [TestTypes]
where [Type] = @type_no;
/* 2. build dynamic query string */
-- cast type number as string
declare @s_type varchar(10) = cast(@type_no as varchar(10));
-- sql query string
declare @sql nvarchar(max) = '';
declare @sql_colname nvarchar(max) = '';
-- loop variables
declare @i int = 1; -- loop index
declare @s varchar(10); -- stringified @i
declare @colname varchar(max); -- new column name
set @sql += '
select
L.[ID]';
-- add columns one by one
while @i <= @n begin
set @s = cast(@i as varchar(10));
-- (2-1) find the new column name
SET @sql_colname = N'select @colname = Result' + @s + 'Name
from [TestTypes]
where [Type] = ' + @s_type;
EXEC SP_EXECUTESQL
@Query = @sql_colname,
@Params = N'@colname varchar(max) OUTPUT',
@colname = @colname OUTPUT;
-- (2-2) sql clause of the new column
set @sql += ',
cast(L.Result' + @s + ' as varchar(10)) + '' '' + R.Result' + @s + 'Unit as [' + @colname + ']'
-- next Result
set @i += 1
end
set @sql += '
into [ans]
from [Tests] as L
inner join [TestTypes] as R
on L.TestType = R.Type
where R.[Type] = ' + @s_type;
/* execute */
print @sql; -- check the query string
if OBJECT_ID('testdb..ans') is not null
drop table testdb..ans;
exec sp_sqlexec @sql;
/* show */
select * from [ans];
结果(类型=1)
| ID | Temperature | Variance |
|------|-------------|----------|
| 1001 | 50 F | 29 % |
| 1004 | 20.2 F | 30 % |
/* the query string */
select
L.[ID],
cast(L.Result1 as varchar(10)) + ' ' + R.Result1Unit as [Temperature],
cast(L.Result2 as varchar(10)) + ' ' + R.Result2Unit as [Variance]
into [ans]
from [Tests] as L
inner join [TestTypes] as R
on L.TestType = R.Type
where R.[Type] = 1
在 SQL Server 2017(linux docker 图像,最新版本)上测试,在 debian 10
我有两个 table 为清楚起见,我在下面进行了简化。一个存储数据值,而另一个定义数据的单位和类型。有些测试有一个结果,有些可能有更多(我的实际 table 有结果 1-10):
Table 'Tests':
ID Result1 Result2 TestType(FK to TestTypes Type)
---------- ------------ ----------- -----------
1001 50 29 1
1002 90.9 NULL 2
1003 12.4 NULL 2
1004 20.2 30 1
Table 'TestTypes':
Type TestName Result1Name Result1Unit Result2Name Result2Unit ..........
------- --------- ------------ ----------- ------------ -----------
1 Temp Calib. Temperature F Variance %
2 Clarity Turbidity CU NULL NULL
我想在连接两个 table 时使用 ResultXName 作为列别名。换句话说,如果用户想要查看所有类型 1 'Temp Calib' 测试,则数据格式如下:
Temperature Variance
------------ -----------
50 F 10.1%
20.2 F 4.4%
或者如果他们查看类型 2,它只使用 1 个结果并且应该忽略 NULL:
Turbidity
----------
90.9 CU
12.4 CU
我在合并 table 的两列方面取得了一些成功:
SELECT CONCAT(Result1, ' ', ISNULL(Result1Unit, ''))
FROM Tests
INNER JOIN TestTypes ON Tests.TestType = TestTypes.Type
但我不知道如何使用 TestName 作为新的列别名。这是我一直在尝试使用子查询,但似乎子查询在 AS 子句中是不允许的:
SELECT CONCAT(Result1, ' ', ISNULL(Result1Unit, '')) AS (SELECT TOP(1) Result1Name FROM TestTypes WHERE Type = 1)
FROM Tests
INNER JOIN TestTypes ON Tests.TestType = TestTypes.Type
我可以使用其他方法吗?或者我是否需要重组我的数据来实现这一目标?我正在使用 MSSQL。
是的,这可以通过仔细构建动态 SQL 字符串完全自动化。本方案关键点及参考文献如下。
- Count the Result variables(第 1 节)
- 通过 using
sp_executesql
with theoutput
definition 获取ResultXName
的新列名称(第 2-1 节) - 附加新列的子句(第 2-2 节)
N.B.1。尽管动态 table 架构通常被认为是 糟糕的设计 ,但有时人们只是被命令这样做。因此,我不质疑此要求的充分性。
N.B.2。注意任意字符串执行的安全问题。根据您的用例,可能需要额外的字符串过滤器。
测试数据集
use [testdb];
GO
if OBJECT_ID('testdb..Tests') is not null
drop table testdb..Tests;
create table [Tests] (
[ID] int,
Result1 float,
Result2 float,
TestType int
)
insert into [Tests]([ID], Result1, Result2, TestType)
values (1001,50,29,1),
(1002,90.9,NULL,2),
(1003,12.4,NULL,2),
(1004,20.2,30,1);
if OBJECT_ID('testdb..TestTypes') is not null
drop table testdb..TestTypes;
create table [TestTypes] (
[Type] int,
TestName varchar(50),
Result1Name varchar(50),
Result1Unit varchar(50),
Result2Name varchar(50),
Result2Unit varchar(50)
)
insert into [TestTypes]([Type], TestName, Result1Name, Result1Unit, Result2Name, Result2Unit)
values (1,'Temp Calib.','Temperature','F','Variance','%'),
(2,'Clarity','Turbidity','CU',NULL,NULL);
--select * from [Tests];
--select * from [TestTypes];
解决方案
/* Input Parameter */
declare @type_no int = 1;
/* 1. determine the number of Results */
declare @n int;
-- If there are hundreds of results please use the method as of (2-1)
select @n = LEN(COALESCE(LEFT(Result1Name,1),''))
+ LEN(COALESCE(LEFT(Result2Name,1),''))
FROM [TestTypes]
where [Type] = @type_no;
/* 2. build dynamic query string */
-- cast type number as string
declare @s_type varchar(10) = cast(@type_no as varchar(10));
-- sql query string
declare @sql nvarchar(max) = '';
declare @sql_colname nvarchar(max) = '';
-- loop variables
declare @i int = 1; -- loop index
declare @s varchar(10); -- stringified @i
declare @colname varchar(max); -- new column name
set @sql += '
select
L.[ID]';
-- add columns one by one
while @i <= @n begin
set @s = cast(@i as varchar(10));
-- (2-1) find the new column name
SET @sql_colname = N'select @colname = Result' + @s + 'Name
from [TestTypes]
where [Type] = ' + @s_type;
EXEC SP_EXECUTESQL
@Query = @sql_colname,
@Params = N'@colname varchar(max) OUTPUT',
@colname = @colname OUTPUT;
-- (2-2) sql clause of the new column
set @sql += ',
cast(L.Result' + @s + ' as varchar(10)) + '' '' + R.Result' + @s + 'Unit as [' + @colname + ']'
-- next Result
set @i += 1
end
set @sql += '
into [ans]
from [Tests] as L
inner join [TestTypes] as R
on L.TestType = R.Type
where R.[Type] = ' + @s_type;
/* execute */
print @sql; -- check the query string
if OBJECT_ID('testdb..ans') is not null
drop table testdb..ans;
exec sp_sqlexec @sql;
/* show */
select * from [ans];
结果(类型=1)
| ID | Temperature | Variance |
|------|-------------|----------|
| 1001 | 50 F | 29 % |
| 1004 | 20.2 F | 30 % |
/* the query string */
select
L.[ID],
cast(L.Result1 as varchar(10)) + ' ' + R.Result1Unit as [Temperature],
cast(L.Result2 as varchar(10)) + ' ' + R.Result2Unit as [Variance]
into [ans]
from [Tests] as L
inner join [TestTypes] as R
on L.TestType = R.Type
where R.[Type] = 1
在 SQL Server 2017(linux docker 图像,最新版本)上测试,在 debian 10