我可以将 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 函数来识别同时具有 bikemotorbike 标签的行。 所以我的示例中所需的输出将只是第一行:

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),所以原始查询变得 指数级 越慢,你扔给它的行越多。