Пример...
Create Table QryTable (ID [int], Description [nvarchar](255), CSV_Vals [nvarchar](255))
insert into QryTable
values
(1, 'Description of record #1', 'val_1,val_2,val_3,val_4,val_5,val_6'),
(2, 'Description of record #2', 'val_1,val_3,val_6,val_9,val_10,val_11'),
(3, 'Description of record #3', 'val_2,val_3,val_4,val_15,val_20,val_21')
Create Table CriterionTbl (ID [int] identity (1,1), CriterionVals [nvarchar](50))
insert into CriterionTbl values ('val_3', 'val_4')
Функциональность аналогична:
Select *
from QryTable
where CSV_vals like `AND (SELECT CriterionVals from CriterionTbl)`
Результаты будут такими же, как если бы запрос был написан, за исключением того, что независимые значения являются результатами запроса таблицы CriterionVals, и возвращаются только записи, которые включают все значения, перечисленные в таблице CriterionVals, а не записи, которые включают любое из значений. в таблице CriterionVals:
Select *
from QryTable
where CSV_vals like '%val_3%'
and CSV_vals like '%val_4%'
Я могу использовать следующий запрос для получения записей с любым значением в CriterionTbl, но мне нужно написать запрос, который возвращает результаты записей, имеющих все критерии из подзапроса CriterionTbl.
Вот что у меня есть, что возвращает записи с чем-либо в CriterionTbl:
select *
from QryTable
Join (select * from CriterionTbl as CT)
on QryTable.CSV_Vals like '%' + CT.CriterionVals + '%'
Это возвращает: записи 1, 2 и 3, потому что все 1, 2 и 3 имеют либо «val_3», либо «val_4». Но мне нужны только записи 1 и 3, потому что только 1 и 3 имеют «val_3» и «val_4».
Но мне нужно, чтобы это происходило из подзапроса, чтобы результаты основывались на содержимом таблицы CriterionVals.
Эта рабочий пример может помочь нам совместно найти решение dbfiddle.uk/AZzsprzJ
возможно это поможет sqlshack.com/dynamic-sql-in-sql-server
Корень проблемы здесь — ваш дизайн. Вы нарушили 1NF, сохранив данные с разделителями подобным образом. Если бы это было правильно нормализовано, это было бы просто.


Обычно условие любого критерия применяется с помощью WHERE EXISTS(SELECT * FROM Criteria WHERE criteria-passes), а условие всех критериев можно применить с помощью WHERE NOT EXISTS(SELECT * FROM Criteria WHERE NOT criteria-passes).
Я считаю, что в этом случае вам нужно последнее:
select *
from QryTable QT
where not exists (
select *
from CriterionTbl CT
where QT.CSV_Vals not like '%' + CT.CriterionVals + '%'
where ',' + QT.CSV_Vals + ',' not like '%,' + CT.CriterionVals + ',%'
)
Я добавил запятые в приведенный выше тест, чтобы избежать ложных совпадений между такими строками, как «val_2» и «val_20».
Также можно использовать оператор ALL() с небольшими уговорами и выражением CASE, которое отображает успех или неудачу в 1 или 0.
select *
from QryTable QT
where 1 = all (
select case when ',' + QT.CSV_Vals + ',' like '%,' + CT.CriterionVals + ',%' then 1 else 0 end
from CriterionTbl CT
)
Стоит отметить, что если CriterionTbl пусто, условие всегда будет истинным.
Результаты одинаковы для обоих вышеперечисленных вариантов:
См. эту db<>fiddle для демонстрации. (Спасибо @BartMcEndree за первоначальную настройку скрипки.)
Жаль, что это требует использования двойного отрицания. Трудно было сразу понять, почему это работает. Хотелось бы более простого решения.
Иногда полезно знать правила распределения булевой логики: NOT(A AND B) = (NOT A OR NOT B), NOT(A OR B) = (NOT A AND NOT B) и аналогично ANY(X) = NOT ALL(NOT X) и ALL(X) = NOT ANY(NOT X). Последнее применимо и здесь.
Или думайте об этом как «все проходит» = «ничего не вышло».
Спасибо, @TN за решение. Я предоставил упрощенную версию того, над чем работаю, и первое предоставленное вами решение не сработало в моем случае использования, однако второе сработало: выберите * из QryTable QT, где 1 = все (выберите регистр, когда ', ' + QT.CSV_Vals + ',' например '%,' + CT.CriterionVals + ',%', затем 1 else 0 заканчивается из CriterionTbl CT) Я пытаюсь создать поисковый запрос, в котором условия поиска разделены во входных данных строка с & для всех требуемых критериев или + для любого требуемого критерия. Удалось использовать ваше второе решение для сопоставления с несколькими полями.
Примечание. Хотя первое решение, предоставленное TN, не сработало в моем конкретном случае использования, оно сработало в приведенном мной примере (который представляет собой упрощенную версию моего варианта использования). Я думаю, что возросшая сложность данных, которые я запрашиваю, привела к неожиданным результатам первого решения TN. Итак, хотя это и не сработало с моим реальным набором данных, оно отлично работает для примера, который я предоставил. Еще раз спасибо, ТН!
это не идеально, просто другой подход. но один из вариантов — разделить данные, сравнить с ключевыми словами и выбрать записи, в которых счетчик соответствует всем условиям.
declare @QryTable Table (ID [int], Description [nvarchar](255), CSV_Vals [nvarchar](255))
insert into @QryTable
VALUES
(1, 'Description of record #1', 'val_1,val_2,val_3,val_4,val_5,val_6'),
(2, 'Description of record #2', 'val_1,val_3,val_6,val_9,val_10,val_11'),
(3, 'Description of record #3', 'val_2,val_3,val_4,val_15,val_20,val_21')
declare @CriterionTbl Table (ID [int] identity (1,1), CriterionVals [nvarchar](50))
insert into @CriterionTbl VALUES ('val_3'), ('val_4');
declare @maxCount int = 0;
select @maxCount = Count(*) from @CriterionTbl
Select * from @QryTable
where id in (
select ID FROM
(
select rn = ROW_NUMBER() over ( partition by id order by id),
*
from
(select id, CSV_Vals, value from @QryTable cross apply String_split(CSV_Vals, ',')) t
inner join
(select CriterionVals FROM @CriterionTbl ) ct on t.value = ct.CriterionVals
) x
group by id having count(id) = @maxCount
)
ОБНОВЛЕНИЕ: (спасибо, T.N) для упрощенной версии я добавлю к ответу - чтобы ее можно было найти
)
-- count(distinct ...) is used to allow handle possible duplicates in either table
declare @maxCount int = (select Count(distinct CriterionVals) from CriterionTbl)
Select qt.*
from QryTable qt
where @maxCount = (
select count(distinct ct.CriterionVals)
from String_split(qt.CSV_Vals, ',') t
inner join CriterionTbl ct
on t.value = ct.CriterionVals
)
результат будет
Вышеуказанное является хорошим подходом, который позволяет избежать обмана сопоставления с разделителями-запятыми. Код можно значительно упростить, исключив пару вложенных запросов. Изменение count(*) на count(distinct CriterionVals) также будет обрабатывать случаи, когда одна или обе таблицы имеют повторяющиеся значения. рабочий пример.
@TN, спасибо за ваш комментарий, вы правы, вы бы удалили дубликаты, и я очень ценю настройку скрипки. кроме того, упрощенный подход выглядит потрясающе.. всегда узнаешь что-то новое.
См. пример. Там мы можем использовать любой подходящий подзапрос вместо CriterionTbl в качестве inner join (select x as CriterionVals from anyTable ) as c.
select *
from(
select q.*
,row_number()over(partition by q.id order by q.id) rn
,count(*)over(partition by q.id) qty
from QryTable q
inner join CriterionTbl c
on concat(',',CSV_vals,',') like concat('%,',c.CriterionVals,',%')
)t
where qty=(select count(*) from CriterionTbl) and rn=1
Там путем расчетаrow_number()over(partition by q.id order by q.id) rn мы можем получить любую 1 строку из результата соединения.
Значение count(*)over(partition by q.id) qty — для строк фильтра, где все значения из CtriterionTbl соответствуют CSV_vals.
Мы не используем distinct или group by для повторяющихся значений в CriterionVals, поскольку это не влияет на результат.
Демо (на примере @TN)
Или
select *
from(
select q.*
,row_number()over(partition by q.id order by q.id) rn
from QryTable q
inner join CriterionTbl c
on concat(',',csv_vals,',') like concat('%,',c.criterionvals,',%')
)t
where rn=(select count(*) from CriterionTbl)
Спасибо всем, @TN предоставил решение, которое я смог применить.
В моем случае я создаю хранимую процедуру, которая будет принимать строку параметров с условиями поиска, разделенными & или +.
Если они разделены знаком &, то каждый поисковый запрос должен быть включен в набор результатов.
Если они разделены знаком +, то в каждый возвращаемый результат должен быть включен один поисковый запрос.
Используя мою упрощенную исходную таблицу в качестве примера, вот полное решение, которое я реализовал:
Create Table QryTable (ID [int], Description [nvarchar](255), Vals_1 [nvarchar](255), Vals_2 [nvarchar](255), Vals_3 [nvarchar](255))
insert into QryTable
values
(1, 'Description of record #1', 'val_1 val_2 val_3','val_4 val_5 val_6'),
(2, 'Description of record #2', 'val_1 val_3 val_6','val_9 val_10 val_11'),
(3, 'Description of record #3', 'val_2 val_3 val_4','val_15 val_20 val_21')
(4, 'Description of record #4', 'val_2 val_5 val_9','val_15 val_20 val_21')
(5, 'Description of record #5', 'val_2 val_8 val_5','val_15 val_20 val_21')
(6, 'Description of record #6', 'val_2 val_6 val_11','val_1 val_20 val_3')
declare @AndOr int = 0
declare @Search_Table table (s_id int IDENTITY(1,1), searchTerm nvarchar(50))
--to return results with any term separated by +
declare @SearchString nvarchar(255) = 'val_1+val_3+val7'
--to return results with all terms separated by &
declare @SearchString nvarchar(255) = 'val_1&val_3&val7'
IF CHARINDEX('&',@SearchString) > 0
SET @AndOr =1; SET @SearchString = replace(@SearchString, '&&', '|')
IF CHARINDEX('+',@SearchString) > 0
SET @SearchString = replace(@SearchString, '++', '|')
insert into @Search_Table SELECT distinct value FROM string_split(@SearchString,'|') ST
if @AndOr = 0
SELECT *
FROM QryTable QT
JOIN @Search_Table ST
ON ( QT.Vals_1 LIKE '%' + ST.SearchString + '%'
OR QT.Vals_2 LIKE '%' + ST.SearchString + '%'
OR QT.Vals_3 LIKE '%' + ST.SearchString + '%'
)
if @AndOr = 1
SELECT *
FROM QryTable QT
WHERE 1=ALL (
SELECT CASE WHEN QT.Vals_1 LIKE '%' + ST.SearchString + '%'
OR QT.Vals_2 LIKE '%' + ST.SearchString + '%'
OR QT.Vals_3 LIKE '%' + ST.SearchString + '%'
THEN 1 ELSE 0 END
FROM @Search_Table ST
)
Это решение позволяет пользователю вводить ряд значений для поиска, например: val_1&val_3&val_6, которые возвращают только записи 1 и 6. Однако, если пользователь вводит строку, но заменяет & на + val1+val_3+val_6, результат будет следующим: записи: 1, 2, 3, 6
Ваши команды вставки недействительны. Во второй таблице только одна строка с двумя значениями или есть две строки с одним значением в каждой?