我可以将 string_split 与强制标签组合一起使用吗?
Can I use string_split with enforcing combination of labels?
所以我有以下 table:
Id Name Label
---------------------------------------
1 FirstTicket bike|motorbike
2 SecondTicket bike
3 ThirdTicket e-bike|motorbike
4 FourthTicket car|truck
我想使用 string_split 函数来识别同时具有 bike 和 motorbike 标签的行。
所以我的示例中所需的输出将只是第一行:
Id Name Label
--------------------------------------
1 FirstTicket bike|motorbike
目前,我正在使用以下查询,但它返回第 1、2 和 3 行。我只想要第一行。可能吗?
SELECT Id, Name, Label FROM tickets
WHERE EXISTS (
SELECT * FROM STRING_SPLIT(Label, '|')
WHERE value IN ('bike', 'motorbike')
)
您可以为此使用字符串函数:
select t.*
from mytable t
where
'|' + label + '|' like '%|bike|%'
and '|' + label + '|' like '%|motorbike|%'
我希望这比其他拆分和聚合的方法更有效。
但是请注意,您真的应该考虑修复您的数据模型。您应该有一个单独的 table 来表示票证和标签之间的关系,而不是存储分隔列表,每个 ticket/label 元组一行。在数据库列中存储分隔列表是众所周知的 SQL 反模式,应不惜一切代价避免这种模式(难以维护、难以查询、难以实施数据完整性、效率低下……)。您可以查看 this famous SO post 以了解有关此主题的更多信息。
您可以使用 APPLY
并进行聚合:
SELECT t.id, t.FirstTicket, t.Label
FROM tickets t CROSS APPLY
STRING_SPLIT(t.Label, '|') t1
WHERE t1.value IN ('bike', 'motorbike')
GROUP BY t.id, t.FirstTicket, t.Label
HAVING COUNT(DISTINCT t1.value) = 2;
但是,这违反了规范化规则,您应该有单独的 table 票。
Yogesh 先于我;我的解决方案是相似的,但是 HUGE 性能改进值得指出。我们将从以下示例数据开始:
SET NOCOUNT ON;
IF OBJECT_ID('tempdb..#tickets','U') IS NOT NULL DROP TABLE #tickets;
CREATE TABLE #tickets (Id INT, [Name] VARCHAR(50), Label VARCHAR(1000));
INSERT #tickets (Id, [Name], Label)
VALUES
(1,'FirstTicket' , 'bike|motorbike'),
(2,'SecondTicket', 'bike'),
(3,'ThirdTicket' , 'e-bike|motorbike'),
(4,'FourthTicket', 'car|truck'),
(5,'FifthTicket', 'motorbike|bike');
现在是原始版本和大大改进的版本:
-- Original
SELECT t.id, t.[Name], t.Label
FROM #tickets AS t
CROSS APPLY STRING_SPLIT(t.Label, '|') t1
WHERE t1.[value] IN ('bike', 'motorbike')
GROUP BY t.id, t.[Name], t.Label
HAVING COUNT(DISTINCT t1.[value]) = 2;
-- Improved Version Leveraging APPLY to avoid a sort
SELECT t.Id, t.[Name], t.Label
FROM #tickets AS t
CROSS APPLY
(
SELECT 1
FROM STRING_SPLIT(t.Label,'|') AS split
WHERE split.[value] IN ('bike','motorbike')
HAVING COUNT(*) = 2
) AS isMatch(TF);
现在执行计划:
如果比较成本:"sortless" 版本的查询速度比原始版本快 4.36 倍。实际上更多是因为,在第一个版本中,我们不只是排序,我们正在对三列进行排序——一个 int 和两个 (n)varchar。因为排序成本是 N * LOG(N),所以原始查询变得 指数级 越慢,你扔给它的行越多。
所以我有以下 table:
Id Name Label
---------------------------------------
1 FirstTicket bike|motorbike
2 SecondTicket bike
3 ThirdTicket e-bike|motorbike
4 FourthTicket car|truck
我想使用 string_split 函数来识别同时具有 bike 和 motorbike 标签的行。 所以我的示例中所需的输出将只是第一行:
Id Name Label
--------------------------------------
1 FirstTicket bike|motorbike
目前,我正在使用以下查询,但它返回第 1、2 和 3 行。我只想要第一行。可能吗?
SELECT Id, Name, Label FROM tickets
WHERE EXISTS (
SELECT * FROM STRING_SPLIT(Label, '|')
WHERE value IN ('bike', 'motorbike')
)
您可以为此使用字符串函数:
select t.*
from mytable t
where
'|' + label + '|' like '%|bike|%'
and '|' + label + '|' like '%|motorbike|%'
我希望这比其他拆分和聚合的方法更有效。
但是请注意,您真的应该考虑修复您的数据模型。您应该有一个单独的 table 来表示票证和标签之间的关系,而不是存储分隔列表,每个 ticket/label 元组一行。在数据库列中存储分隔列表是众所周知的 SQL 反模式,应不惜一切代价避免这种模式(难以维护、难以查询、难以实施数据完整性、效率低下……)。您可以查看 this famous SO post 以了解有关此主题的更多信息。
您可以使用 APPLY
并进行聚合:
SELECT t.id, t.FirstTicket, t.Label
FROM tickets t CROSS APPLY
STRING_SPLIT(t.Label, '|') t1
WHERE t1.value IN ('bike', 'motorbike')
GROUP BY t.id, t.FirstTicket, t.Label
HAVING COUNT(DISTINCT t1.value) = 2;
但是,这违反了规范化规则,您应该有单独的 table 票。
Yogesh 先于我;我的解决方案是相似的,但是 HUGE 性能改进值得指出。我们将从以下示例数据开始:
SET NOCOUNT ON;
IF OBJECT_ID('tempdb..#tickets','U') IS NOT NULL DROP TABLE #tickets;
CREATE TABLE #tickets (Id INT, [Name] VARCHAR(50), Label VARCHAR(1000));
INSERT #tickets (Id, [Name], Label)
VALUES
(1,'FirstTicket' , 'bike|motorbike'),
(2,'SecondTicket', 'bike'),
(3,'ThirdTicket' , 'e-bike|motorbike'),
(4,'FourthTicket', 'car|truck'),
(5,'FifthTicket', 'motorbike|bike');
现在是原始版本和大大改进的版本:
-- Original
SELECT t.id, t.[Name], t.Label
FROM #tickets AS t
CROSS APPLY STRING_SPLIT(t.Label, '|') t1
WHERE t1.[value] IN ('bike', 'motorbike')
GROUP BY t.id, t.[Name], t.Label
HAVING COUNT(DISTINCT t1.[value]) = 2;
-- Improved Version Leveraging APPLY to avoid a sort
SELECT t.Id, t.[Name], t.Label
FROM #tickets AS t
CROSS APPLY
(
SELECT 1
FROM STRING_SPLIT(t.Label,'|') AS split
WHERE split.[value] IN ('bike','motorbike')
HAVING COUNT(*) = 2
) AS isMatch(TF);
现在执行计划:
如果比较成本:"sortless" 版本的查询速度比原始版本快 4.36 倍。实际上更多是因为,在第一个版本中,我们不只是排序,我们正在对三列进行排序——一个 int 和两个 (n)varchar。因为排序成本是 N * LOG(N),所以原始查询变得 指数级 越慢,你扔给它的行越多。