如何替换和组合 T-SQL 中多列的值
How to replace and combine the values from multiple columns in T-SQL
我在 SQL Server 2016 中有一个 table,用于安排工作日的会议。它将一周中每一天的日期存储在一列中(下面的示例)。我想查询此 table 并以更 user-friendly 的格式在单个列中获取结果。
当前数据存储在 table 中,如下所示:
SCHEDULE TABLE:
MEETING_TITLE | MONDAY | TUESDAY | WEDNESDAY | THURSDAY | FRIDAY
FOO | NULL | Y | NULL | Y | NULL
我的愿望是在结果中合并这些工作日列,并将它们转换为相应日期的缩写。
想要的结果:
SCHEDULE TABLE:
MEETING_TITLE | MEETING_DAYS
FOO | T/THU
我研究了 case 语句,if/else 并尝试使用声明变量来保存值,但到目前为止没有任何效果。为了展示我解决问题的尝试,我特地尝试声明一个变量来保存格式化的日期。之后,我尝试每天在列中查找 'Y' 时执行 if/else。如果找到了,我希望做一些 += 的事情来根据需要组合这些值。我尝试的一切都导致错误,查询不会 运行.
感谢您对此提供的任何帮助。
(上面的例子当然是简化了,这个table远比上面复杂)
注意:这是 Oracle 语法而不是 sql-server。无论如何将它留在这里供其他可能喜欢它作为 oracle 答案的人使用。
select meeting_title
, CONCAT( decode( monday, 'Y', 'M' ),' ',
decode( tuesday, 'Y', 'T' ),' ',
decode( wednesday, 'Y', 'W' ),' ',
decode( thursday, 'Y', 'Th' ),' ',
decode( friday, 'Y', 'F' ) )
from my_schedule
--You can use CASE Statements to concat and then
--a combination of STUFF and REVERSE to remove the trailing / off [MEETING_DAYS]
SELECT [MEETING_TITLE], reverse(stuff(reverse(
CASE WHEN [Monday] IS NULL THEN '' ELSE 'Mon/' END +
CASE WHEN [Tuesday] IS NULL THEN '' ELSE 'T/' END +
CASE WHEN [Wednesday] IS NULL THEN '' ELSE 'Wed/' END +
CASE WHEN [Thursday] IS NULL THEN '' ELSE 'THU/' END +
CASE WHEN [Friday] IS NULL THEN '' ELSE 'Fri/' END), 1, 1, '')) AS [MEETING_DAYS]
FROM [dbo].[Schedule]
如果您使用的是 SQL 2017 或更高版本,一个简单的实现方法是
select meeting_title,
Concat_Ws('/',
Iif(monday is null,null,'Mon'),
Iif(tuesday is null,null,'tue'),
Iif(wednesday is null,null,'wed'),
Iif(thursday is null,null,'thu'),
Iif(friday is null,null,'fri')
) as meeting_days
from schedule
此版本仅适用于 SQL Server 2017 或更高版本(或者如果您使用自定义字符串聚合函数)。
另一个可能是cross apply
:
select s.*, v.*
from schedule s cross apply
(select string_agg(name, '/') within group (order by ord) as meeting_days
from (values ('M', Monday, 1),
('T', Tuesday, 2),
('W', Wednesday, 3),
('Thu', Thursday, 4),
('F', Friday, 5)
) v(name, val, ord)
where val = 'Y'
) v;
请尝试以下解决方案。
要点:
- 我们正在将感兴趣的列标记为 XML。
FOR XML ...
自动过滤掉具有 NULL 值的列。
- 开始使用 3 个字符来缩写一周中的每一天。
SQL
-- DDL and sample data population, start
DECLARE @tbl TABLE (
ID INT IDENTITY PRIMARY KEY,
meeting VARCHAR(100),
MONDAY CHAR(1),
TUESDAY CHAR(1),
WEDNESDAY CHAR(1),
THURSDAY CHAR(1),
FRIDAY CHAR(1)
);
INSERT INTO @tbl (meeting,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY) VALUES
('FOO', NULL,'Y',NULL,'Y',NULL),
('New Meeting', 'Y','Y',NULL,'Y',NULL);
-- DDL and sample data population, end
SELECT ID, meeting
, REPLACE(c.query('
for $x in /root/*
return substring(local-name($x),1,3)
').value('.', 'VARCHAR(100)'), SPACE(1),'/') AS MEETING_DAYS
FROM @tbl
CROSS APPLY (SELECT MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
FOR XML PATH(''), TYPE, ROOT('root')) AS t(c);
输出
+----+-------------+--------------+
| ID | meeting | MEETING_DAYS |
+----+-------------+--------------+
| 1 | FOO | TUE/THU |
| 2 | New Meeting | MON/TUE/THU |
+----+-------------+--------------+
带有示例数据:
Declare @testData table (
meeting_title varchar(20)
, Monday char(1)
, Tuesday char(1)
, Wednesday char(1)
, Thursday char(1)
, Friday char(1)
);
Insert Into @testData (meeting_title, Monday, Tuesday, Wednesday, Thursday, Friday)
Values ('FEE', Null, 'Y', Null, 'Y', Null)
, ('FIE', 'Y', Null, 'Y', 'Y', Null)
, ('FOE', 'Y', 'Y', Null, Null, 'Y');
Select *
, meeting_days = stuff(concat('/' + iif(td.Monday = 'Y', 'Mon', Null)
, '/' + iif(td.Tuesday = 'Y', 'Tue', Null)
, '/' + iif(td.Wednesday = 'Y', 'Wed', Null)
, '/' + iif(td.Thursday = 'Y', 'Thu', Null)
, '/' + iif(td.Friday = 'Y', 'Fri', Null)), 1, 1, '')
From @testData td;
我在 SQL Server 2016 中有一个 table,用于安排工作日的会议。它将一周中每一天的日期存储在一列中(下面的示例)。我想查询此 table 并以更 user-friendly 的格式在单个列中获取结果。
当前数据存储在 table 中,如下所示:
SCHEDULE TABLE:
MEETING_TITLE | MONDAY | TUESDAY | WEDNESDAY | THURSDAY | FRIDAY
FOO | NULL | Y | NULL | Y | NULL
我的愿望是在结果中合并这些工作日列,并将它们转换为相应日期的缩写。
想要的结果:
SCHEDULE TABLE:
MEETING_TITLE | MEETING_DAYS
FOO | T/THU
我研究了 case 语句,if/else 并尝试使用声明变量来保存值,但到目前为止没有任何效果。为了展示我解决问题的尝试,我特地尝试声明一个变量来保存格式化的日期。之后,我尝试每天在列中查找 'Y' 时执行 if/else。如果找到了,我希望做一些 += 的事情来根据需要组合这些值。我尝试的一切都导致错误,查询不会 运行.
感谢您对此提供的任何帮助。
(上面的例子当然是简化了,这个table远比上面复杂)
注意:这是 Oracle 语法而不是 sql-server。无论如何将它留在这里供其他可能喜欢它作为 oracle 答案的人使用。
select meeting_title
, CONCAT( decode( monday, 'Y', 'M' ),' ',
decode( tuesday, 'Y', 'T' ),' ',
decode( wednesday, 'Y', 'W' ),' ',
decode( thursday, 'Y', 'Th' ),' ',
decode( friday, 'Y', 'F' ) )
from my_schedule
--You can use CASE Statements to concat and then
--a combination of STUFF and REVERSE to remove the trailing / off [MEETING_DAYS]
SELECT [MEETING_TITLE], reverse(stuff(reverse(
CASE WHEN [Monday] IS NULL THEN '' ELSE 'Mon/' END +
CASE WHEN [Tuesday] IS NULL THEN '' ELSE 'T/' END +
CASE WHEN [Wednesday] IS NULL THEN '' ELSE 'Wed/' END +
CASE WHEN [Thursday] IS NULL THEN '' ELSE 'THU/' END +
CASE WHEN [Friday] IS NULL THEN '' ELSE 'Fri/' END), 1, 1, '')) AS [MEETING_DAYS]
FROM [dbo].[Schedule]
如果您使用的是 SQL 2017 或更高版本,一个简单的实现方法是
select meeting_title,
Concat_Ws('/',
Iif(monday is null,null,'Mon'),
Iif(tuesday is null,null,'tue'),
Iif(wednesday is null,null,'wed'),
Iif(thursday is null,null,'thu'),
Iif(friday is null,null,'fri')
) as meeting_days
from schedule
此版本仅适用于 SQL Server 2017 或更高版本(或者如果您使用自定义字符串聚合函数)。
另一个可能是cross apply
:
select s.*, v.*
from schedule s cross apply
(select string_agg(name, '/') within group (order by ord) as meeting_days
from (values ('M', Monday, 1),
('T', Tuesday, 2),
('W', Wednesday, 3),
('Thu', Thursday, 4),
('F', Friday, 5)
) v(name, val, ord)
where val = 'Y'
) v;
请尝试以下解决方案。
要点:
- 我们正在将感兴趣的列标记为 XML。
FOR XML ...
自动过滤掉具有 NULL 值的列。- 开始使用 3 个字符来缩写一周中的每一天。
SQL
-- DDL and sample data population, start
DECLARE @tbl TABLE (
ID INT IDENTITY PRIMARY KEY,
meeting VARCHAR(100),
MONDAY CHAR(1),
TUESDAY CHAR(1),
WEDNESDAY CHAR(1),
THURSDAY CHAR(1),
FRIDAY CHAR(1)
);
INSERT INTO @tbl (meeting,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY) VALUES
('FOO', NULL,'Y',NULL,'Y',NULL),
('New Meeting', 'Y','Y',NULL,'Y',NULL);
-- DDL and sample data population, end
SELECT ID, meeting
, REPLACE(c.query('
for $x in /root/*
return substring(local-name($x),1,3)
').value('.', 'VARCHAR(100)'), SPACE(1),'/') AS MEETING_DAYS
FROM @tbl
CROSS APPLY (SELECT MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
FOR XML PATH(''), TYPE, ROOT('root')) AS t(c);
输出
+----+-------------+--------------+
| ID | meeting | MEETING_DAYS |
+----+-------------+--------------+
| 1 | FOO | TUE/THU |
| 2 | New Meeting | MON/TUE/THU |
+----+-------------+--------------+
带有示例数据:
Declare @testData table (
meeting_title varchar(20)
, Monday char(1)
, Tuesday char(1)
, Wednesday char(1)
, Thursday char(1)
, Friday char(1)
);
Insert Into @testData (meeting_title, Monday, Tuesday, Wednesday, Thursday, Friday)
Values ('FEE', Null, 'Y', Null, 'Y', Null)
, ('FIE', 'Y', Null, 'Y', 'Y', Null)
, ('FOE', 'Y', 'Y', Null, Null, 'Y');
Select *
, meeting_days = stuff(concat('/' + iif(td.Monday = 'Y', 'Mon', Null)
, '/' + iif(td.Tuesday = 'Y', 'Tue', Null)
, '/' + iif(td.Wednesday = 'Y', 'Wed', Null)
, '/' + iif(td.Thursday = 'Y', 'Thu', Null)
, '/' + iif(td.Friday = 'Y', 'Fri', Null)), 1, 1, '')
From @testData td;