【问题标题】:SQL query to change column names to single column value将列名更改为单列值的 SQL 查询
【发布时间】:2021-02-06 22:16:27
【问题描述】:

我正在使用 SQL Server。不知道是什么版本,但它仍然受支持。 我有一个表,其中包含我感兴趣的 3 列。我需要更改“硬件”列中的列名以及每个列的计数。现在我有以下内容:

SELECT 
    COUNT(a.EAMacBook13) AS 'MacBook13', 
    COUNT(a.EAMacBook16) AS 'MacBook16', 
    COUNT(a.EAStandLaptop) AS 'StandLaptop' 
FROM 
    Employee AS e, EmpAttributes AS a 
WHERE 
    a.EAEmpID = e.EmpID AND e.EmpProjID = 1

我需要类似的东西:

这可能吗?

【问题讨论】:

  • 请用您正在运行的数据库标记您的问题:mysql、oracle、postgresql...?
  • Bad habits to kick : using old-style JOINs - 旧式 逗号分隔的表格列表 样式已替换为 ANSI 中的 proper ANSI JOIN 语法-92 SQL 标准(25 多年前),不鼓励使用它
  • 通过在 SSMS 中运行 SELECT @@VERSION 找出您的 SQL Server 的版本

标签: sql sql-server count unpivot lateral-join


【解决方案1】:

基本思想是反透视和计数。如果您的数据库支持横向连接(又名cross apply)和values(),您可以这样做:

select x.hardware, count(x.val) cnt
from employee as e
inner join empattributes as ea on ea.eaempid = e.empid 
cross join lateral (values 
    ('EAMacBook13', ea.EAMacBook13), 
    ('EAMacBook16', ea.EAMacBook16), 
    ('EAStandLaptop', ea.EAStandLaptop)
) x(hardware, val)
where e.empprojid = 1
group by x.hardware

横向连接有不同的可用语法,具体取决于您的数据库。一些数据库使用cross apply 而不是cross join lateral

values() 也不支持所有地方。例如,在 Oracle 中,您可以将横向连接表述为:

cross apply (
    select 'EAMacBook13' as hardware, EAMacBook13 as val from dual
    union all select 'EAMacBook16', EAMacBook16 from dual
    union all select 'EAStandLaptop', EAStandLaptop from dual
) x(hardware)

如果这些都不可用,那么我们可以使用union all

select x.hardware, count(x.val) cnt
from employee as e
inner join (
    select eaempid, 'EAMacBook13' as hardware, EAMacBook13 as value from empattributes 
    union all select eaempid, 'EAMacBook16', EAMacBook16 from empattributes 
    union all select eaempid, 'EAStandLaptop', EAStandLaptop from empattributes 
) x on x.eaempid = e.empid 
where e.empprojid = 1
group by x.hardware

【讨论】:

  • @JimBedson:我看到你现在标记了你的问题 SQL Server。在这个数据库中,您可以使用第一个查询 - 只需将 cross join lateral 替换为 cross apply。这比您接受的答案中的union all 更有效,因为它只扫描表一次。
【解决方案2】:

你可以用一个简单的 union all 来做到这一点:

SELECT 'MacBook13' "Hardware", Count(a.EAMacBook13) "Count"
FROM Employee as e, EmpAttributes as a WHERE a.EAEmpID = e.EmpID and e.EmpProjID = 1
union all 
SELECT 'MacBook16' "Hardware", Count(a.EAMacBook16)  "Count"
FROM Employee as e, EmpAttributes as a WHERE a.EAEmpID = e.EmpID and e.EmpProjID = 1
union all 
SELECT 'StandLaptop' "Hardware", Count(a.EAStandLaptop)  "Count"
FROM Employee as e, EmpAttributes as a WHERE a.EAEmpID = e.EmpID and e.EmpProjID = 1

根据您的 RDBMS,您可能可以访问 UNPIVOT,这会减少编写工作。

您还可以使用 CTE,以便(如果您的 RDBMS 支持实现它们)您可以减少工作量:

with cte as (
  SELECT Count(a.EAMacBook13) as EAMacBook13, Count(a.EAMacBook16) as EAMacBook16, Count(a.EAStandLaptop) as EAStandLaptop
  FROM Employee as e, EmpAttributes as a WHERE a.EAEmpID = e.EmpID and e.EmpProjID = 1
)
SELECT 'MacBook13' "Hardware", EAMacBook13 "Count"
FROM  cte
union all 
SELECT 'MacBook16' "Hardware", MacBook16 "Count"
FROM cte
union all 
SELECT 'StandLaptop' "Hardware", StandLaptop "Count"
FROM cte

除非您使用某种程度的动态 SQL,否则您必须列出可能的硬件值。

【讨论】:

  • 第三个值缺少一个联合,但这完全符合我的需要。谢谢。
【解决方案3】:

一种选择是使用动态反透视。首先用当前查询创建一个表:

SELECT a.EAMacBook13, a.EAMacBook16, a.EAStandLaptop
  INTO tab
  FROM Employee AS e 
  JOIN EmpAttributes AS a 
    ON a.EAEmpID = e.EmpID
 WHERE e.EmpProjID = 1

然后使用information_schema.columns

DECLARE @cols  AS NVARCHAR(MAX),  @query AS NVARCHAR(MAX)

SET  @query = 
    (SELECT STRING_AGG(CONCAT('SELECT ''',SUBSTRING(column_name,3,LEN(column_name)),''' AS Hardware, 
            COUNT(',column_name,') AS Count FROM tab '),' UNION ALL ')
       FROM information_schema.columns
      WHERE table_name = 'tab');

EXEC sp_executesql @query;

Demo

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-09
    • 2019-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多