如何在 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 来实现这一点是可能的,但会有点矫枉过正。
编号 |注册期间 |开始日期 |结束日期 -: | :---------------------------- | :--------- | :--------- 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-31declare @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;
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 脚本中获取环境名称?