【问题标题】:How to extract values from column and update result in another column如何从列中提取值并在另一列中更新结果
【发布时间】:2018-08-30 15:12:28
【问题描述】:

我有这张桌子:

CREATE TABLE MyTable (
    IdDate int,
    FullDate varchar(255)
);

insert into MyTable (IdDate,FullDate)
VALUES (0, 'Nº1 (26) - Friday 4, January 2014'),
       (0,'Nº2 (64) - Monday 10, February 2015')

我想从 FullDate 中提取如下内容:

1 2014 01 04
2 2015 02 10

1st number is extracted from Nº1
2nd number is extracted from Year
3rd number is extracted from Month (convert January to 01)
4th number is extracted from day (if day < 10, add 0 at the beginning: 01,02... )

并更新在名为 IdDate 的第一列中提取的新值

我的最终结果应该是这样的:

IdDate        FullDate
120140104     Nº1 (26) - Friday 4, January 2014
220150210     Nº2 (64) - Monday 10, February 2015

【问题讨论】:

  • 问题的根源在于您将日期存储为字符串。数据类型的存在是有原因的,不要对自己这样做。 sqlblog.org/2009/10/12/…
  • 是的,我知道。但我别无选择。当我获得新的 IdDate 时,我将删除 FullDate 列
  • 你能分享一些实际的样本数据吗?意思是创建表脚本后跟几个插入语句。哦,天哪....我认为您的专栏实际上有所有额外的垃圾???
  • 二月???哦不。
  • 共享表脚本

标签: sql sql-server split


【解决方案1】:

如果对辅助表值函数开放:

示例

Declare @YourTable table (IdDate int,FullDate varchar(max))
Insert Into @YourTable values
 (0,'Nº1 (26) - Friday 4, January 2014')
,(0,'Nº2 (64) - Monday 10, February 2015')

Update A
   set IdDate = substring(Pos1,3,10)
              + try_convert(varchar(10),try_convert(date,Pos6+' '+Pos5+' '+Pos7),112)
 From  @YourTable A
 Cross Apply [dbo].[tvf-Str-Parse-Row](FullDate,' ') B

退货

IDDate      FullDate
120140104   Nº1 (26) - Friday 4, January 2014
220150210   Nº2 (64) - Monday 10, February 2015

如果它有助于可视化,则 TVF 会返回

感兴趣的功能

CREATE FUNCTION [dbo].[tvf-Str-Parse-Row] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (
    Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
          ,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
          ,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
          ,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
          ,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
          ,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
          ,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
          ,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(max)')))
          ,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(max)')))
    From  (Select Cast('<x>' + replace((Select replace(@String,@Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A 
)

或者没有函数

Update A
   set IdDate = substring(Pos1,3,10)
              + try_convert(varchar(10),try_convert(date,Pos6+' '+Pos5+' '+Pos7),112)
 From  @YourTable A
 Cross Apply (
                Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
                      ,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
                      ,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
                      ,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
                      ,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
                      ,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
                      ,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
                From  (Select Cast('<x>' + replace((Select replace(FullDate,' ','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A 
             ) B

编辑

这是 Shawn cleaner 解决方案的扩展版本

Update @YourTable 
   set IdDate = substring(left(FullDate,charindex(' ',FullDate)-1),3,25)
               +try_convert(varchar(10),try_convert(date,replace(substring(FullDate, charindex(',', FullDate) - 2, 100), ',', '')),112)


Select * from @YourTable

【讨论】:

  • 感谢您的回复,但是当我运行您的代码时,显示 IdDate = Null。不像你的截图
  • 如果转换失败,@RaulEscalona try_convert() 将返回 NULL。日期拼错?额外的空间?像 6 月 31 日这样的虚假日期?
  • 我只是复制并粘贴您的第一个代码。并显示 Null
  • @RaulEscalona 这里是一个 dbFiddle dbfiddle.uk/…
  • 它在您的链接中正常工作,但在我的数据库中却没有。我正在使用 SQL SERVER Express 2017
【解决方案2】:

这将提取一个日期值。它只是查找逗号,备份几个字符并获取日期,去掉逗号并将其视为军事日期。

select convert(date,  
   replace(substring(FullDate, charindex(',', FullDate) - 2, 100), ',', ''), 106)

使用 format() 或日期样式 112 以您需要的方式获得输出。第一个字符显然只是substring(FullDate, 3, 1),所以只需将其附加到前面即可。

【讨论】:

    【解决方案3】:

    如果您必须在不使用 John 发布的函数(我会这样做)的情况下解决这个问题,您可以深入挖掘并开始一条噩梦般的字符串函数路径。挑战在于 t-sql 不擅长字符串操作。

    这似乎适用于您提供的示例数据。请注意,如果您有无法转换为日期的数据,则会失败。

    update MyTable
    set IdDate = substring(FullDate, 3, CHARINDEX(' ', FullDate) - 3)
        + Right(FullDate, 4)
        + right('0' + convert(varchar(2), datepart(month, convert(date, replace(substring(substring(FullDate, charindex('-', FullDate) + 2, len(FullDate)), charindex(' ', substring(FullDate, charindex('-', FullDate) + 2, len(FullDate))) + 1, len(FullDate)), ',', '')))), 2)
        + right('0' + convert(varchar(2), datepart(day, convert(date, replace(substring(substring(FullDate, charindex('-', FullDate) + 2, len(FullDate)), charindex(' ', substring(FullDate, charindex('-', FullDate) + 2, len(FullDate))) + 1, len(FullDate)), ',', '')))), 2)
    
    select * from MyTable
    

    然后去问问决定像这样存储数据的人为什么这样做?这根本不是处理数据的方式。

    【讨论】:

    • 在日期和/小时中转换字符串时出错。我的专栏 FullDate 是 varchar(255)
    • 你一定有一些垃圾数据。使用您的示例数据和表格可以正常工作。
    • 当我得到新的 IdDate 时,我会删除 FullDate 列
    • 喂!!!你将从一个糟糕的设计转向另一个。停止疯狂!这是两条信息。一个数字和一个日期。把它们塞在一起仍然违反 1NF。把它分成两列,否则你会继续遇到问题。
    【解决方案4】:
    1st number is extracted from Nº1
    2nd number is extracted from Year
    3rd number is extracted from Month (convert January to 01)
    4th number is extracted from day (if day < 10, add 0 at the beginning: 01,02... )
    

    使用SQL Server's string functions 执行以下操作

    1. 获取 与其后的第一个空格之间的字符。
    2. 获取最后 4 个字符
    3. 使用 CASE 表达式根据 LIKE 字符串的月份名称生成月份数
    4. 获取逗号前的 2 个字符,并将其中的任何空白字符替换为 0

    并将这 4 个值连接在一起。

    【讨论】:

    • 但是我该怎么做呢?
    • docs.microsoft.com/en-us/sql/t-sql/functions/…(也添加到答案中)。你问了一个广泛的问题,我给了你一个广泛的策略。使用此链接进行一些研究并尝试解决问题。如果您对这些技术中的任何一种感到困惑,请将其作为一个新问题,以便您可以将问题集中在一种特定技术上。
    • 我不同意 Tab. OP不知道该怎么做。他们提供了功能性的 ddl 和示例数据。很清楚他们期望的输出是什么。这些数据是如此完整的火车残骸,要解开这个烂摊子并不容易。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多