SQL将Varchar转换为日期

huangapple go评论77阅读模式
英文:

SQL Convert Varchar to Date

问题

我有一个查询,通过子字符串从大型存储字段中获取 yyyy-MM-dd 格式的值,因此该值自动为 varchar 类型。以下是整个记录的示例内容:

EmployeeName$Test User
Email$test@test.com
ADUsername$tuser
TermDate$2023-07-13
EmployeeNumber$12345
Department$Tech

以下 SQL 查询有效,并正确返回所有数据,其中 TermDateString 从不为 null,且似乎始终格式正确:

with cte as (
    select i.IncidentNumber,
    case when charindex('TermDate$',i.symptom) = 0 or charindex('EmployeeNumber$',i.symptom) = 0 then null else
    substring(symptom,charindex('TermDate$',i.symptom) + 9,(charindex('EmployeeNumber$',i.symptom) - 2) - (charindex('TermDate$',i.symptom) + 9)) 
    end as 'TermDateString'
    from Incident i
	inner join task t on t.parentlink_recid = i.recid
	where i.subject like '%term in proc%'
    and i.status not in ('closed','cancelled','resolved')
    and i.symptom like '%EmployeeName%'
    and t.subject = 'Open WFH Equipment Return Request'
    and t.status not in ('closed','cancelled')
    --and i.incidentnumber = '5305093'
)
select *
--,convert(date,TermDateString,23) 'TermDate'
--,cast(TermDateString as date) 'TermDate'
from cte
--where convert(date,TermDateString,23) > DATEADD(month, -4, GETDATE())
order by incidentnumber desc

然而,我只需要获取在特定时间范围内的工单。当将 varchar 转换为日期或日期时间时,取消注释 --,convert(date,TermDateString,23) 'TermDate' 后,它总是抛出此错误:

转换字符字符串为日期和/或时间时出错。

我认为这与特定记录的数据无关,因为它从不为 null,我已检查没有前导/尾随空格。此外,我手动测试了多个记录,取消注释 --and i.incidentnumber = '5305093',这是顶部结果,以及第二个结果。分别来看,这两个都有效。然而,当使用 --and i.incidentnumber = 'xxxx' 仍然被注释掉时,执行 Select top 2 i.incidentnumber 会抛出相同的错误,这意味着仅在尝试处理多个记录时才会出错。我还尝试使用 CAST 而不是 CONVERT,但结果完全相同。

是否有人知道如何使其正常工作?

英文:

I have a query that grabs a yyyy-MM-dd format from a large storage field by substringing, so the value is automatically a varchar. An example of the entire record's contents is below:

EmployeeName$Test User
Email$test@test.com
ADUsername$tuser
TermDate$2023-07-13
EmployeeNumber$12345
Department$Tech

The following SQL query works and returns all data correctly, where TermDateString is never null and appears to be always formatted correctly:

with cte as (
    select i.IncidentNumber,
    case when charindex('TermDate$',i.symptom) = 0 or charindex('EmployeeNumber$',i.symptom) = 0 then null else
    substring(symptom,charindex('TermDate$',i.symptom) + 9,(charindex('EmployeeNumber$',i.symptom) - 2) - (charindex('TermDate$',i.symptom) + 9)) 
    end as 'TermDateString'
    from Incident i
	inner join task t on t.parentlink_recid = i.recid
	where i.subject like '%term in proc%'
    and i.status not in ('closed','cancelled','resolved')
    and i.symptom like '%EmployeeName%'
    and t.subject = 'Open WFH Equipment Return Request'
    and t.status not in ('closed','cancelled')
    --and i.incidentnumber = '5305093'
)
select *
--,convert(date,TermDateString,23) 'TermDate'
--,cast(TermDateString as date) 'TermDate'
from cte
--where convert(date,TermDateString,23) > DATEADD(month, -4, GETDATE())
order by incidentnumber desc

SQL将Varchar转换为日期

However, I need to only get tickets where TermDateString is within a certain time frame. When converting the varchar to a date or datetime, by uncommenting --,convert(date,TermDateString,23) 'TermDate', it always throws this error:

> Conversion failed when converting date and/or time from character string.

I believe that it is not related to a specific record's data, as it is never null and I checked there are no leading/trailing white spaces. Additionally, I manually tested multiple records by uncommenting --and i.incidentnumber = '5305093', which is the top result, as well as the 2nd result. Individually, both of those worked. However when doing a Select top 2 i.incidentnumber with --and i.incidentnumber = 'xxxx' still commented out, it throws the same error, meaning it only throws an error when it is trying to handle multiple records. I have also tried using CAST instead of CONVERT, but with the exact same results.

Does anybody know of a way to get this working?

答案1

得分: 2

我认为当“TermDateString”列中存在无效的日期值时,会出现以下错误:

> 转换字符字符串中的日期和/或时间失败

要解决此问题并检索特定时间范围内的工单,您可以按如下方式修改查询:

SQL

WITH cte AS (
    SELECT i.IncidentNumber,
    CASE WHEN CHARINDEX('TermDate$', i.symptom) = 0 OR CHARINDEX('EmployeeNumber$', i.symptom) = 0 THEN NULL ELSE
    SUBSTRING(symptom, CHARINDEX('TermDate$', i.symptom) + 9, (CHARINDEX('EmployeeNumber$', i.symptom) - 2) - (CHARINDEX('TermDate$', i.symptom) + 9)) 
    END AS 'TermDateString'
    FROM Incident i
    INNER JOIN task t ON t.parentlink_recid = i.recid
    WHERE i.subject LIKE '%term in proc%'
    AND i.status NOT IN ('closed', 'cancelled', 'resolved')
    AND i.symptom LIKE '%EmployeeName%'
    AND t.subject = 'Open WFH Equipment Return Request'
    AND t.status NOT IN ('closed', 'cancelled')
)
SELECT *
FROM cte
WHERE TRY_CONVERT(date, TermDateString) BETWEEN '2021-01-01' AND '2021-12-31'
ORDER BY IncidentNumber DESC;

在这个修改后的版本中,我用TRY_CONVERT(date, TermDateString)替换了转换函数,它会优雅地处理任何无效的日期值,并返回NULL,而不会抛出错误。另外,我添加了一个BETWEEN条件,用于过滤在您期望的时间范围内的“TermDateString”值(在本例中为从2021年1月1日到2021年12月31日)。您可以根据自己的需求调整日期范围。

通过使用TRY_CONVERTBETWEEN条件,查询应该可以成功执行,检索在指定时间范围内的工单,而不会遇到任何转换错误。

英文:

I think that The error

> Conversion failed when converting date and/or time from character string

occurs when there are invalid date values in the TermDateString column. To resolve this issue and retrieve tickets within a specific time frame, you can modify your query as follows:

sql

WITH cte AS (
    SELECT i.IncidentNumber,
    CASE WHEN CHARINDEX('TermDate$', i.symptom) = 0 OR CHARINDEX('EmployeeNumber$', i.symptom) = 0 THEN NULL ELSE
    SUBSTRING(symptom, CHARINDEX('TermDate$', i.symptom) + 9, (CHARINDEX('EmployeeNumber$', i.symptom) - 2) - (CHARINDEX('TermDate$', i.symptom) + 9)) 
    END AS 'TermDateString'
    FROM Incident i
    INNER JOIN task t ON t.parentlink_recid = i.recid
    WHERE i.subject LIKE '%term in proc%'
    AND i.status NOT IN ('closed', 'cancelled', 'resolved')
    AND i.symptom LIKE '%EmployeeName%'
    AND t.subject = 'Open WFH Equipment Return Request'
    AND t.status NOT IN ('closed', 'cancelled')
)
SELECT *
FROM cte
WHERE TRY_CONVERT(date, TermDateString) BETWEEN '2021-01-01' AND '2021-12-31'
ORDER BY IncidentNumber DESC;
```

In this modified version, I replaced the conversion function with TRY_CONVERT(date, TermDateString), which gracefully handles any invalid date values and returns NULL instead of throwing an error. Additionally, I added a BETWEEN condition to filter the TermDateString values within your desired time frame (in this case, from January 1, 2021, to December 31, 2021). You can adjust the date range according to your requirements.

By using TRY_CONVERT and the BETWEEN condition, the query should execute successfully, retrieving the tickets within the specified time frame without encountering any conversion errors.

答案2

得分: 0

如果数据无法修复,您可以通过将 $ 替换为 ":"\r\n 替换为 ":",并将结果包装在 {"}" 中,将其转换为 JSON:

declare @field nvarchar(3000)='EmployeeName$Test User
Email$test@test.com
ADUsername$tuser
TermDate$2023-07-13
EmployeeNumber$12345
Department$Tech'

declare @as_json varchar(3000)='{"' + replace(replace(@field,'$','":"'),CHAR(13)+CHAR(10),'","') + '"}'

使用此表达式将字段转换为 JSON:

'{"' + replace(replace(@field,'$','":"'),CHAR(13)+CHAR(10),'","') + '"}'

您可以使用单个 JSON_VALUE 调用提取 TermDate 并直接将其转换为 date

select cast(JSON_VALUE(@as_json,'$.TermDate') as date)

或者,作为单个表达式:

select cast(JSON_VALUE('{"' + replace(replace(@field,'$','":"'),CHAR(13)+CHAR(10),'","') + '"}','$.TermDate') as date)

如果您经常需要执行此操作,甚至可以使用完整表达式创建计算列,甚至可以对其进行索引。

您可以创建一个函数来将该格式转换为 JSON,以简化表达式的编写:

create function To_Json(@field nvarchar(4000))
returns nvarchar(4000)
as
BEGIN
	return '{"' + replace(replace(@field,'$','":"'),CHAR(13)+CHAR(10),'","') + '"}'
END

然后使用它:

select cast(JSON_VALUE(dbo.To_Json(@field),'$.TermDate') as date)

您还可以创建一个函数,直接检索您想要的路径:

create function Get_By_Path(@field nvarchar(4000),@path nvarchar(200))
returns nvarchar(4000)
as
BEGIN
	declare @json nvarchar(4000)= '{"' + replace(replace(@field,'$','":"'),CHAR(13)+CHAR(10),'","') + '"}'
    return JSON_VALUE(@json,@path)
END

并在查询中使用它:

select cast(dbo.get_by_path(@field,'$.TermDate') as date)
英文:

If the data can't be fixed, you can convert it to JSON by replacing $ with ":", \r\n with ":" and surround the result with {"and}"`:

declare @field nvarchar(3000)='EmployeeName$Test User
Email$test@test.com
ADUsername$tuser
TermDate$2023-07-13
EmployeeNumber$12345
Department$Tech'

declare @as_json varchar(3000)='{"' + replace(replace(@field,'$','":"'),CHAR(13)+CHAR(10),'","') + '"}'

The field is converted to JSON with this expression:

'{"' + replace(replace(@field,'$','":"'),CHAR(13)+CHAR(10),'","') + '"}'

You can extract the TermDate with a single call to JSON_VALUE and cast it directly to a date.

select cast(JSON_VALUE(@as_json,'$.TermDate') as date)

Or, as a single expression:

select cast(JSON_VALUE('{"' + replace(replace(@field,'$','":"'),CHAR(13)+CHAR(10),'","') + '"}','$.TermDate') as date)

If you have to do this frequently, you can even use the complete expression to create a computed column and even index it.

You can create a function to convert that format to JSON, to make it easier to write expressions:

create function To_Json(@field nvarchar(4000))
returns nvarchar(4000)
as
BEGIN
	return '{"' + replace(replace(@field,'$','":"'),CHAR(13)+CHAR(10),'","') + '"}'
END

And use it :

select cast(JSON_VALUE(dbo.To_Json(@field),'$.TermDate') as date)

You could also create a function that retrieves the path you want directly:

create function Get_By_Path(@field nvarchar(4000),@path nvarchar(200))
returns nvarchar(4000)
as
BEGIN
	declare @json nvarchar(4000)= '{"' + replace(replace(@field,'$','":"'),CHAR(13)+CHAR(10),'","') + '"}'
    return JSON_VALUE(@json,@path)
END

And use it in a query:

select cast(dbo.get_by_path(@field,'$.TermDate') as date)

huangapple
  • 本文由 发表于 2023年7月13日 22:08:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76680322.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定