如何操作字符串 a T-SQL?
How can I manipulate a string a T-SQL?
business_id
open_day
open_time
close_day
close_time
1
FRIDAY
08:00
FRIDAY
12:00
1
FRIDAY
13:00
FRIDAY
17:00
1
MONDAY
08:00
MONDAY
17:00
2
SATURDAY
08:00
SATURDAY
16:00
2
SUNDAY
08:00
SUNDAY
16:00
3
MONDAY
08:00
MONDAY
16:00
我的任务是为企业创建和设置营业时间格式,我需要将这些时间按天分组在一个字符串中。然而,它正在重复这一天。如果某个值在一个字符串中出现两次,是否可以搜索该值?我已经走到这一步了:
create table open_times (business_id int, open_day varchar(10), open_time varchar(10), close_day varchar(10), close_time varchar(10) )
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (1, 'FRIDAY', '08:00', 'FRIDAY', '12:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (1, 'FRIDAY', '13:00', 'FRIDAY', '17:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (1, 'MONDAY', '08:00', 'MONDAY', '17:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (2, 'SATURDAY', '08:00', 'SATURDAY', '16:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (2, 'SUNDAY', '08:00', 'SUNDAY', '16:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (3, 'MONDAY', '08:00', 'MONDAY', '16:00')
drop table open_times
感谢您的帮助。
select
business_id
,left(name_values, LEN(name_values)-1) as opening_hours
from
(
select
results.business_id
,STUFF((
select
( case when open_day = 'FRIDAY' then 'Fr' when open_day = 'MONDAY' then 'Mo' when open_day = 'TUESDAY' then 'Tu' when open_day = 'WEDNESDAY' then 'We' When open_day = 'THURSDAY' then 'Th' when open_day = 'SATURDAY' then 'Sa' else 'Su' end )
+ ' ' + open_time + '-' + close_time + '; '
from open_times
where business_id = results.business_id
for xml path(''),type).value('(./text())[1]','VARCHAR(MAX)'),1,0, '') as name_values
from open_times results
group by business_id
) innerquery
Current Output for Business 1: 'Fr 08:00-12:00; Fr 13:00-17:00; Mo 08:00-17:00'
Desired Output For Business 1: 'Fr 08:00-12:00, 13:00-17:00; Mo 08:00-17:00'
你可以在这里使用STRING_AGG
。你只需要做两级分组,每天一次,然后对整个 business_id
SELECT
ot.business_id,
Times = STRING_AGG(CONCAT(
UPPER(LEFT(ot.open_day, 1)),
LOWER(SUBSTRING(ot.open_day, 2, 1)),
' ',
ot.Times)
, '; ')
FROM (
SELECT
ot.business_id,
ot.open_day,
Times = STRING_AGG(CONCAT(
ot.open_time,
'-',
ot.close_time),
', ')
FROM open_times ot
GROUP BY ot.business_id, ot.open_day
) ot
GROUP BY ot.business_id;
对于旧版本,请使用 FOR XML PATH
聚合技巧。假设每天不超过 2 个打开间隔
with t as (
select business_id, left(open_day,2) + ' ' + min(open_time + '-' + close_time) +
case when min(open_time) = max(open_time) then ''
else ', ' + max(open_time + '-' + close_time) end ots
from open_times
group by business_id, open_day
)
select business_id, stuff(
(select '; ' + ots
from t t2
where t2.business_id = t1.business_id
order by left(ots,2)
for xml path(''))
, 1, 2, '') opening_hours
from t t1
group by business_id
您可以通过将 window 函数合并到您的逻辑中来使用一级聚合来执行此操作:
select ot.business_id,
stuff((select (case when seqnum = 1 then '; ' else ', ' end) +
(case when seqnum = 1 and ot2.open_day = 'FRIDAY' then 'Fr '
when seqnum = 1 and ot2.open_day = 'MONDAY' then 'Mo '
when seqnum = 1 and ot2.open_day = 'TUESDAY' then 'Tu '
when seqnum = 1 and ot2.open_day = 'WEDNESDAY' then 'We '
when seqnum = 1 and ot2.open_day = 'THURSDAY' then 'Th '
when seqnum = 1 and ot2.open_day = 'SATURDAY' then 'Sa '
when seqnum = 1 then 'Su '
else ''
end ) +
ot2.open_time + '-' + ot2.close_time
from (select ot2.*,
row_number() over (partition by ot2.open_day order by ot2.open_time) as seqnum
from open_times ot2
where ot2.business_id = ot.business_id
) ot2
order by ot2.open_day, ot2.open_time
for xml path(''),type
).value('(./text())[1]', 'VARCHAR(MAX)'), 1, 2, ''
) as name_values
from open_times ot
group by ot.business_id;
Here 是一个 db<>fiddle.
您的查询版本实际上不正确,因为您在 XML 子查询中没有 order by
。结果可以是任意顺序。
此外,您不仅要删除重名,还想将分隔符从 ;
更改为 ,
。
上述查询的思路是枚举每天的时间。分隔符第一次是;
,后面的分隔符是,
。该缩写仅用于第一个。我还为所有列引用添加了限定条件,这是强烈推荐的做法。
请注意,您可以将 case
逻辑简化为:
left(ot2.open_day, 1) + lower(substring(ot2.open_day, 2, 1))
business_id | open_day | open_time | close_day | close_time |
---|---|---|---|---|
1 | FRIDAY | 08:00 | FRIDAY | 12:00 |
1 | FRIDAY | 13:00 | FRIDAY | 17:00 |
1 | MONDAY | 08:00 | MONDAY | 17:00 |
2 | SATURDAY | 08:00 | SATURDAY | 16:00 |
2 | SUNDAY | 08:00 | SUNDAY | 16:00 |
3 | MONDAY | 08:00 | MONDAY | 16:00 |
我的任务是为企业创建和设置营业时间格式,我需要将这些时间按天分组在一个字符串中。然而,它正在重复这一天。如果某个值在一个字符串中出现两次,是否可以搜索该值?我已经走到这一步了:
create table open_times (business_id int, open_day varchar(10), open_time varchar(10), close_day varchar(10), close_time varchar(10) )
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (1, 'FRIDAY', '08:00', 'FRIDAY', '12:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (1, 'FRIDAY', '13:00', 'FRIDAY', '17:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (1, 'MONDAY', '08:00', 'MONDAY', '17:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (2, 'SATURDAY', '08:00', 'SATURDAY', '16:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (2, 'SUNDAY', '08:00', 'SUNDAY', '16:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (3, 'MONDAY', '08:00', 'MONDAY', '16:00')
drop table open_times
感谢您的帮助。
select
business_id
,left(name_values, LEN(name_values)-1) as opening_hours
from
(
select
results.business_id
,STUFF((
select
( case when open_day = 'FRIDAY' then 'Fr' when open_day = 'MONDAY' then 'Mo' when open_day = 'TUESDAY' then 'Tu' when open_day = 'WEDNESDAY' then 'We' When open_day = 'THURSDAY' then 'Th' when open_day = 'SATURDAY' then 'Sa' else 'Su' end )
+ ' ' + open_time + '-' + close_time + '; '
from open_times
where business_id = results.business_id
for xml path(''),type).value('(./text())[1]','VARCHAR(MAX)'),1,0, '') as name_values
from open_times results
group by business_id
) innerquery
Current Output for Business 1: 'Fr 08:00-12:00; Fr 13:00-17:00; Mo 08:00-17:00'
Desired Output For Business 1: 'Fr 08:00-12:00, 13:00-17:00; Mo 08:00-17:00'
你可以在这里使用STRING_AGG
。你只需要做两级分组,每天一次,然后对整个 business_id
SELECT
ot.business_id,
Times = STRING_AGG(CONCAT(
UPPER(LEFT(ot.open_day, 1)),
LOWER(SUBSTRING(ot.open_day, 2, 1)),
' ',
ot.Times)
, '; ')
FROM (
SELECT
ot.business_id,
ot.open_day,
Times = STRING_AGG(CONCAT(
ot.open_time,
'-',
ot.close_time),
', ')
FROM open_times ot
GROUP BY ot.business_id, ot.open_day
) ot
GROUP BY ot.business_id;
对于旧版本,请使用 FOR XML PATH
聚合技巧。假设每天不超过 2 个打开间隔
with t as (
select business_id, left(open_day,2) + ' ' + min(open_time + '-' + close_time) +
case when min(open_time) = max(open_time) then ''
else ', ' + max(open_time + '-' + close_time) end ots
from open_times
group by business_id, open_day
)
select business_id, stuff(
(select '; ' + ots
from t t2
where t2.business_id = t1.business_id
order by left(ots,2)
for xml path(''))
, 1, 2, '') opening_hours
from t t1
group by business_id
您可以通过将 window 函数合并到您的逻辑中来使用一级聚合来执行此操作:
select ot.business_id,
stuff((select (case when seqnum = 1 then '; ' else ', ' end) +
(case when seqnum = 1 and ot2.open_day = 'FRIDAY' then 'Fr '
when seqnum = 1 and ot2.open_day = 'MONDAY' then 'Mo '
when seqnum = 1 and ot2.open_day = 'TUESDAY' then 'Tu '
when seqnum = 1 and ot2.open_day = 'WEDNESDAY' then 'We '
when seqnum = 1 and ot2.open_day = 'THURSDAY' then 'Th '
when seqnum = 1 and ot2.open_day = 'SATURDAY' then 'Sa '
when seqnum = 1 then 'Su '
else ''
end ) +
ot2.open_time + '-' + ot2.close_time
from (select ot2.*,
row_number() over (partition by ot2.open_day order by ot2.open_time) as seqnum
from open_times ot2
where ot2.business_id = ot.business_id
) ot2
order by ot2.open_day, ot2.open_time
for xml path(''),type
).value('(./text())[1]', 'VARCHAR(MAX)'), 1, 2, ''
) as name_values
from open_times ot
group by ot.business_id;
Here 是一个 db<>fiddle.
您的查询版本实际上不正确,因为您在 XML 子查询中没有 order by
。结果可以是任意顺序。
此外,您不仅要删除重名,还想将分隔符从 ;
更改为 ,
。
上述查询的思路是枚举每天的时间。分隔符第一次是;
,后面的分隔符是,
。该缩写仅用于第一个。我还为所有列引用添加了限定条件,这是强烈推荐的做法。
请注意,您可以将 case
逻辑简化为:
left(ot2.open_day, 1) + lower(substring(ot2.open_day, 2, 1))