英文:
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
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_CONVERT
和BETWEEN
条件,查询应该可以成功执行,检索在指定时间范围内的工单,而不会遇到任何转换错误。
英文:
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)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论