如何在 t-sql (Azure Synapse) 的 CAST/CONVERT 中使用字符串函数

Posted

技术标签:

【中文标题】如何在 t-sql (Azure Synapse) 的 CAST/CONVERT 中使用字符串函数【英文标题】:How to use string function within CAST/CONVERT in t-sql (Azure Synapse) 【发布时间】:2021-12-22 03:48:32 【问题描述】:

我在 Azure Synapse 中有一张表,如下所示,

想要将 Enrolled_period 列查询为 2 个不同的列作为 DATE 数据类型。

我正在尝试以下查询

  select convert(varchar, SUBSTRING(enrolled_period, 2, 12), 23) as startdate
from dbo.test_period;

也尝试拆分数据

select 
REPLACE(PARSENAME(REPLACE(enrolled_period, ', ', '.'), 2), '(', '') AS startdate, 
REPLACE(PARSENAME(REPLACE(enrolled_period, ', ', '.'), 1), ')', '') AS enddate 
from dbo.test_period

但出现错误

从字符串转换日期和/或时间时转换失败。

我该如何解决这个问题?

【问题讨论】:

enrolled_period 是什么数据类型?另外,请注意日期文字周围有' 表示它们是字符串(需要转换为日期),字符串本身不应包含' 字符。所以,你想要从字符 3 开始的 10 个字符,而不是从字符 2 开始的 12 个字符。然后你想 CAST/COVERT 到一个日期,而不是一个 VARCHAR。 我们正在将数据从 teradata 迁移到 Synapse。 Teradata(bteq) 具有称为周期的数据类型。那是登记的_时期列是。但是在 Synapse(t-sql) 中不支持 period 数据类型。所以将其存储为字符串 最好将其存储为两个日期列。退回以字符串表示形式存储数据总是充满问题。尽可能避免它。 【参考方案1】:

我创建了一个测试表并尝试了这个,它的工作原理:

请检查:

select 
cast ( SUBSTRING(p,3,10) as date) , cast ( SUBSTRING(p,16,10) as date),p
from test2

【讨论】:

【参考方案2】:

在尝试转换/转换为日期之前,最好先测试生成的子字符串是什么样子的。

例如:

declare @enrolled_period varchar(30);
set @enrolled_period = '(''2021-10-11'', ''2021-10-31'')';

select @enrolled_period as enrolled_period
, substring(@enrolled_period, 3, 10) as start1
, substring(@enrolled_period, 17, 10) as end1
, RIGHT(PARSENAME(REPLACE(@enrolled_period, ''', ''', '.'), 2), 10) as start2
, LEFT(PARSENAME(REPLACE(@enrolled_period,  ''', ''', '.'), 1), 10) as end2

select @enrolled_period as enrolled_period
, CAST(SUBSTRING(@enrolled_period, 3, 10) AS DATE) as startdate
, CAST(SUBSTRING(@enrolled_period, 17, 10) AS DATE) as enddate

子字符串的第三个参数是长度,而不是位置。

额外

只是为了展示使用 STRING_SPLIT 或 JSON 来实现这一点是可能的,但会有点矫枉过正。

declare @T table (
  id int identity(1,1) primary key,
  enrolled_period varchar(30)
);

insert into @T (enrolled_period) values
('(''2021-10-11'', ''2021-10-31'')')
;

SELECT t.*, ep.startDate, ep.endDate
FROM @T t
OUTER APPLY (
   select 
    min(try_cast(substring(value,3,10) as date)) as startDate, 
    max(try_cast(substring(value,3,10) as date)) as endDate
   from string_split(t.enrolled_period,',') s
) ep;


SELECT t.*, ep.*
FROM @T t
OUTER APPLY (
  select v.js as period
  , try_cast(json_value(v.js,'$[0]') as date) as startDate
  , try_cast(json_value(v.js,'$[1]') as date) as endDate
  from (
    select replace(replace(replace(t.enrolled_period,'''','"'),'(','['),')',']') as js
  ) v
) ep;
编号 |注册期间 |开始日期 |结束日期 -: | :---------------------------- | :--------- | :--------- 1 | ('2021-10-11', '2021-10-31') | 2021-10-11 | 2021-10-31 编号 |注册期间 |期间 |开始日期 |结束日期 -: | :---------------------------- | :---------------------------- | :--------- | :--------- 1 | ('2021-10-11', '2021-10-31') | [“2021 年 10 月 11 日”,“2021 年 10 月 31 日”] | 2021-10-11 | 2021-10-31

db小提琴here

【讨论】:

我的错!!!我甚至在考虑子字符串中的引号,因此出现错误。谢谢!!!如果我们确实选择 cast('2019-02-01' as date) 作为开始日期,则此方法有效。将此作为参考也考虑引号 我们如何使用 string_split 函数而不是 parsename ?? @Kavyashree 我添加了一些额外的内容。但这只是为了麻烦。因为我们可以确定该字段中的格式,因为它来自我理解的 teradata 类型。我的 Golfcoder 偏爱子字符串方式。 SELECT Enrolled_period, ordinal=1 then cast(replace(replace(value, '(''', ''), '''', '') as DATETIME2) end as startdate, ordinal=2 然后 cast(replace(replace(value, ''')', ''), '''', '') as DATETIME2) end as enddate FROM dbo.test_period CROSS APPLY STRING_SPLIT(Enrolled_period, ', ', 1) 按 Enrolled_period 排序;我以这种方式放置 string_split() 。这看起来更容易。让我知道你的想法 当然,但不是我。但我认为 UDF 只能返回 1 个值或一个表。所以它可能会接受 2 个参数,句点字符串和开始/结束日期返回的标志。【参考方案3】:
SELECT d.person_Id,
       d.University_Name,
       CAST(LEFT(d.Enrolled_period, CHARINDEX (',', d.Enrolled_period) - 1) AS DATE) AS Start_Date,
       CAST(RIGHT(d.Enrolled_period, CHARINDEX (',', d.Enrolled_period) - 1) AS DATE) AS End_Date
FROM
       (
           SELECT d.person_Id,
                  d.University_Name,
                  (REPLACE (REPLACE (REPLACE (d.Enrolled_period, '(', ''), ')', ''), '''', '')) AS Enrolled_period
           FROM
                  (
                      SELECT 24569 AS person_Id,
                             'Gothenburg university' AS University_Name,
                             '(''2007-08-09'',''2009-08-25'')' AS Enrolled_period
                      UNION
                      SELECT 24568 AS person_Id,
                             'Gothenburg university' AS University_Name,
                             '(''2019-07-09'',''2021-06-25'')' AS Enrolled_period
                  ) AS d
       ) AS d

【讨论】:

【参考方案4】:

你需要去掉“-”

select D.[start_date]
    ,D.[end_date]
from dbo.test_period T
    outer apply
    (
        select 
            replace(replace(replace(replace(replace(replace(
                T.enrolled_period
                ,'(', '')
                ,')', '')
                ,'''', '')
                ,',', '')
                ,'-', '')
                ,' ', '') as clean_period
    ) C
    outer apply
    (
        select
            cast(left(C.clean_period, 8) as date) as [start_date]
            ,cast(right(C.clean_period, 8) as date) as [end_date]
    ) D

【讨论】:

实际上,ISO 8601 日期文字包括连字符...en.wikipedia.org/wiki/ISO_8601 @MatBailie,你是对的。但是连字符存在一个问题。它不适用于日期,因为日期不支持 ydm。试试这个:设置 dateformat ydm;选择演员('2018-08-25' 作为日期时间)。 -- 所以这不是一个好习惯。

以上是关于如何在 t-sql (Azure Synapse) 的 CAST/CONVERT 中使用字符串函数的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Synapse (Azure SQL DW) 上检索视图定义?

Azure Synapse 管道:如何将增量更新从 SQL Server 移动到 Synapse 以处理数字

如何使用 Azure Synapse 在 Databricks 上删除表或删除行?

(Azure Synapse) 如何在 SQL 脚本中获取环境名称?

如何在 Databricks 上将 Azure Synapse Dataframe 转换为 JSON?

如何在 Azure Synapse 或数据工厂管道中设置和获取变量值