【问题标题】:How to split comma delimited data from one column into multiple rows如何将逗号分隔的数据从一列拆分为多行
【发布时间】:2021-10-19 23:30:02
【问题描述】:

我正在尝试编写一个查询,该查询将使一列根据另一个逗号分隔的列显示特定值。这些代码用于表示常规时间/加班时间/双倍时间/等,它们来自前面提到的逗号分隔列。在原始视图中,每个不同的小时数都有单独的列。为此,我们可以说 A = 常规时间,B = 双倍时间,C = 加时。但是,我们有很多代码可以表示相同类型的时间。

我原来的视图是什么样的:

Employee_FullName EmpID Code Regular Time Double Time Overtime
John Doe 123 A,B 7 2 0
Jane Doe 234 B 4 0 1

我的查询输出:

Employee_FullName EmpID Code Hours
John Doe 123 A, B 10
John Doe 123 A, B 5
Jane Doe 234 B 5

我希望输出的样子:

Employee_FullName EmpID Code Hours
John Doe 123 A 10
John Doe 123 B 5
Jane Doe 234 B 5

它看起来像在第一个表中的方式,因为目前它只是从常规时间列中提取。我尝试使用大小写开关让它查找特定代码然后提取数字,但无论我如何编写它都会出现各种错误。这是我的查询的样子:

SELECT [Employee_FullName],
    SUBSTRING(col, 1, CHARINDEX(' ', col + ' ' ) -1)'Code',
    hrsValue
FROM
    (
    SELECT [Employee_FullName], col, hrsValue
    FROM myTable
    CROSS APPLY
    (
        VALUES ([Code],[RegularHours])
    ) C (COL, hrsValue)
) SRC

感谢任何有关如何修复它的建议或对使用什么的看法!

编辑:我无法更改逗号分隔的数据,它是以这种方式提供的。我认为交叉申请中的案例会解决它,但老实说我不知道​​。

编辑 2:我将使用唯一的 EmployeeID 来识别他们。在这种情况下,是 A 是常规时间,B 是双倍时间,C 是加班时间。复杂之处在于,每种类型的时间都有多种不同的代码和多个指代。永远不会有 A 会指代一名员工的正常时间和另一名员工的双倍时间等。我在 SQL Server 2017 上。谢谢大家的时间!

【问题讨论】:

  • 有没有案例在你尝试? Show 向我们展示这个案例的尝试。
  • 我不知道从哪里开始放一个箱子。我尝试创建一个临时变量来保存一个代表代码的值,但是我必须找到一种方法让它读取逗号分隔的列。这是最好的方法吗?
  • 您可以在VALUES 语句中将列名替换为大小写表达式。
  • 如果您想让人们真正轻松地提供帮助,请为您的示例数据提供 DDL+DML。
  • 什么版本的 SQL Server?您还可以提供一些示例数据/加班所需的结果吗?如果你有两个 John Do,会发生什么?您真的使用 FullName 来识别员工吗?

标签: sql sql-server tsql


【解决方案1】:

如果您使用的是 SQL Server 2016 或更高版本,则可以使用OPENJSON() 来拆分代码值,而不是繁琐的字符串操作:

SELECT t.Employee_FullName,
       Code = LTRIM(j.value),
       Hours = MAX(CASE j.[key] 
                   WHEN 0 THEN RegularTime
                   WHEN 1 THEN DoubleTime
                   WHEN 2 THEN Overtime END)
  FROM dbo.MyTable AS t
  CROSS APPLY OPENJSON('["' + REPLACE(t.Code,',','","') + '"]') AS j
  GROUP BY t.Employee_FullName, LTRIM(j.value);

【讨论】:

  • 这个解决方案运行得非常优雅,完全符合我的要求。非常感谢您的帮助!
【解决方案2】:

您可以使用以下代码来拆分值

  • 注意如果NULLIF 返回 0,CHARINDEX 是如何消除它的
  • 第二个 APPLY 的后半部分以该 null 为条件
SELECT
  t.[Employee_FullName],
  Code = TRIM(v2.Code),
  v2.Hours
FROM myTable t
CROSS APPLY (VALUES( NULLIF(CHARINDEX(',', t.Code), 0) )) v1(comma)
CROSS APPLY (
    SELECT Code = ISNULL(LEFT(t.Code, v1.comma - 1), t.Code), Hours = t.RegularTime
    UNION ALL
    SELECT SUBSTRING(t.Code, v1.comma + 1, LEN(t.Code)), t.DoubleTime
      WHERE v1.comma IS NOT NULL
) v2;

db<>fiddle

【讨论】:

    【解决方案3】:

    您可以采用基于 CROSS APPLY 的方法,如下所示。

    感谢@Chalieface 的插入脚本。

    CREATE TABLE mytable (
      "Employee_FullName" VARCHAR(8),
      "Code" VARCHAR(3),
      "RegularTime" INTEGER,
      "DoubleTime" INTEGER,
      "Overtime" INTEGER
    );
    
    INSERT INTO mytable
      ("Employee_FullName", "Code", "RegularTime", "DoubleTime", "Overtime")
    VALUES
      ('John Doe', 'A,B', '10', '5', '0'),
      ('Jane Doe', 'B', '5', '0', '0');
    
      SELECT
      t.[Employee_FullName],
      c.Code,
      CASE WHEN c.code = 'A' THEN t.RegularTime
           WHEN c.code = 'B' THEN t.DoubleTime
           WHEN c.code = 'C' THEN t.Overtime
           END AS Hours
      FROM myTable t
    CROSS APPLY (select value from string_split(t.code,',')
    ) c(code)
    
    Employee_FullName Code Hours
    John Doe A 10
    John Doe B 5
    Jane Doe B 0

    【讨论】:

    • 这不是理想的结果(您只使用了RegularTime我认为 OP 希望那里的第二行具有来自DoubleTime 的值):@ 987654322@
    • @AaronBertrand,对不起。我已经更正了答案。感谢您的检查。顺便说一句,我很喜欢你的 SQL Server 帖子。我特别喜欢你的 TSQL 坏习惯。感谢您为 SQL Server 社区提供的服务。
    • 谢谢!但是 Jane Doe 应该有 5,而不是 0,至少根据问题 - 我不太确定 A 或 B 意味着什么,它只是映射到哪个小时的列表中的位置。但我仍然认为样本数据/预期结果可能有问题。
    • @AaronBertrand,是的。同意您。样本数据和预期输出存在混淆。
    猜你喜欢
    • 1970-01-01
    • 2019-11-10
    • 2018-04-13
    • 2018-11-02
    • 1970-01-01
    • 1970-01-01
    • 2018-11-11
    • 1970-01-01
    • 2012-01-24
    相关资源
    最近更新 更多