Могу ли я использовать string_split с принудительной комбинацией меток? - PullRequest
0 голосов
/ 12 февраля 2020

Итак, у меня есть следующая таблица:

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')
      )

Ответы [ 3 ]

1 голос
/ 12 февраля 2020

Вы можете использовать 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;

Однако это нарушает правила нормализации, у вас должны быть отдельные столовые билеты.

1 голос
/ 12 февраля 2020

Йог sh избили меня до этого; мое решение аналогично, но стоит отметить улучшение производительности ОГРОМНОЕ . Мы начнем с этого примера данных:

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);

Теперь планы выполнения:

enter image description here

Если сравнить затраты: версия без сортировки - запрос в 4,36 раза быстрее, чем оригинал. В действительности это больше, потому что в первой версии мы не просто сортируем, мы сортируем три столбца - int и два (n) varchar s. Поскольку затраты на сортировку равны N * LOG (N), исходный запрос становится экспоненциально медленнее, чем больше строк вы выбрасываете.

1 голос
/ 12 февраля 2020

Вы можете просто использовать строковые функции для этого:

select t.*
from mytable t
where 
    '|' + label + '|' like '%|bike|%'
    and '|' + label + '|' like '%|motorbike|%'

Я ожидаю, что это будет более эффективным, чем другие методы, которые разделяют и агрегируют.

Обратите внимание, однако, что вы действительно следует рассмотреть возможность исправления вашей модели данных. Вместо того, чтобы хранить списки с разделителями, у вас должна быть отдельная таблица для представления связи между тикетами и метками, с одной строкой на кортеж тикета / метки. Хранение списков с разделителями в столбце базы данных является хорошо известным SQL антипаттерном, которого следует избегать любой ценой (трудно поддерживать, трудно запрашивать, трудно обеспечить целостность данных, неэффективно и т. Д.). Вы можете взглянуть на этот знаменитый пост SO , чтобы узнать больше об этом топи c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...