【问题标题】:Using Subqueries to Define Column Alias使用子查询定义列别名
【发布时间】:2020-06-12 22:42:54
【问题描述】:

为了清楚起见,我在下面简化了两个表格。一个存储数据值,而另一个定义数据的单位和类型。有些测试有一个结果,有些可能有更多(我的实际表格有结果 1-10):

表“测试”:

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

表'TestTypes':

Type        TestName         Result1Name    Result1Unit   Result2Name     Result2Unit   ..........
-------     ---------        ------------   -----------   ------------    -----------
1           Temp Calib.      Temperature    F             Variance        %
2           Clarity          Turbidity      CU            NULL            NULL

当我加入这两个表时,我想使用 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

我在合并表格的两列方面取得了一些成功:

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。

【问题讨论】:

  • 您将很难在查询中执行此操作(可能是动态的)。我会尝试将所有列组合起来,然后在 SSRS 之类的报告工具中进行格式化(在规则上显示/隐藏列)。查询不能很好地处理不同数量的列和数据类型
  • 哦,你真的不想在一个列中组合一个数字和一个单位。这意味着它现在是一个字符串而不是一个数字。我知道您将 90.9 CU + 12.4 CU 视为 103.3 CU,但这不是它在 SQL 中的工作方式。那将是字符串连接与加法。同样,您可以在报告工具中使用额外的列作为显示列来执行此操作,但将值保存在自己的列中。
  • 对于我的应用程序,不需要保留数据类型。它仅用作显示器。也就是说,我可能会将两列分开,以防我稍后想对数据进行计算。我正在使用 Ignition SCADA 软件 - 我确实可以通过脚本控制列名。我的计划 B 是只使用编写一个 python 脚本来相应地更新标题。
  • 10.1%的方差是怎么来的?

标签: sql sql-server subquery alias


【解决方案1】:

是的,这可以通过仔细构建动态 SQL 字符串来完全自动化。本解决方案的要点及参考如下。

  1. Count the Result variables(第 1 节)
  2. 通过using sp_executesql with the output definition获取ResultXName的新列名(第2-1节)
  3. 为新列附加子句(第 2-2 节)

注 1。虽然动态表模式通常被认为是一种糟糕的设计,但有时人们只是被命令这样做。因此,我不质疑这一要求的充分性。

注意 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

在 debian 10 上的 SQL Server 2017(linux docker 镜像,最新版本)上测试

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-20
    • 2017-03-09
    • 2021-08-16
    • 1970-01-01
    相关资源
    最近更新 更多