Все/любая реализация в SQL наряду с IN

Здравствуйте, мне нужно реализовать несколько запросов через dapper в структуре, аналогичной приведенному ниже псевдокоду.

// Tags
[{id: 1, name: "Tech"}, {id: 2, name: "SQL"}, {id: 3, name: "C#"}]

// BlogPost [
{
  Id: 1
  Tags: [1, 2] // Tech, Sql
},
{
   Id: 2,
   Tags: [1,3] // Tech, C#  
},
{
   Id: 3,
   Tags: [1,2,3] // Text, Sql, C#
}]

Учитывая этот запрос

SELECT 
    [Blogpost].*
From BlogPost blogPost
    LEFT JOIN BlogPostTags tags ON tags.blogId = blogPost.Id
WHERE blogpost.Tags IN (1,2)

Выполняя вышеуказанный запрос, я ожидал бы такого результата. [{blogId: 1}, {blogId: 2}, {BlogId: 3}]

  • Мне нужно получить результат, похожий на [{blogId: 1}], учитывая те же параметры (1,2) в приведенном выше запросе.

  • Также мне нужно получить результат, например сообщение [{blogId: 1}, {BlogId: 3}] с учетом параметров (1,2) в приведенном выше запросе.

Есть ли у sql (MSSQL) какой-нибудь изящный способ получить этот результат?

Или как я могу получить эти результаты эффективно, поскольку в реальном запросе у меня будет больше соединений?

Спасибо!

LEFT JOIN BlogPostTags tags = tags.blogId = blogPost.Id ты имеешь в виду LEFT JOIN BlogPostTags tags ON tags.blogId = blogPost.Id? (Первое значение заменено на ON.)
Thom A 22.02.2024 13:54

Не совсем понятно, о чем вы здесь спрашиваете. Некоторые примеры данных (в DDL и DML) и ожидаемые результаты, вероятно, помогут нам помочь вам здесь.

Thom A 22.02.2024 13:56

Вам нужно решение SQL или решение ORM?

Salman Arshad 22.02.2024 13:58

Я ищу реализацию SQL. Редактировать. Я попытаюсь изучить концепцию реляционного разделения.

user3116167 22.02.2024 14:22
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
4
87
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Подобные запросы довольно неудобно выражать в SQL (примечание: я бы избегал здесь LEFT OUTER JOIN, так как это может привести к дублированию строк).

«содержит любой» — это нормально (обычно через EXISTS (SELECT 1 FROM BlogPostTags tags WHERE tags.blogId = blogPost.Id AND tags.Tags IN @tags), используя автоматическое расширение @tags в Dapper) — однако вы запрашиваете «содержит все» и «содержит все и ничего больше»; они намного сложнее; «Содержит все» обычно представляет собой несколько операций EXISTS, а «содержит все и ничего больше» может быть тем же самым «содержит все» с дополнительным NOT EXISTS (SELECT 1 FROM BlogPostTags tags WHERE tags.blogId = blogPost.Id AND tags.Tags NOT IN @tags)

Однако! Я бы еще раз подчеркнул: эта операция не идеальна в SQL. Для разметки постов Stack Overflow (которая по сути идентична этой) мы перевернули это с ног на голову, добавив отдельный индекс/сервер, который существовал исключительно для выполнения операций с тегами, включая предварительную загрузку всех данных постов/тегов в ОЗУ в оптимизированном режиме. способы и индексация по тегу (обработка дельта-обновлений); это позволяет выполнять множество операций в памяти, используя различные трюки. Гораздо больше усилий и работы, чем простой запрос SQL, но и гораздо более эффективно.

Большое спасибо. Очень хорошие материалы, я рассмотрю.

user3116167 22.02.2024 14:35

Стандартные методы реляционного разделения работают нормально, не знаю, почему вы так боитесь этих запросов.

Charlieface 26.02.2024 11:02
Ответ принят как подходящий

«Содержит любой» — это простой запрос EXISTS.

var tagIds = new[]{ 1, 2, };

using connection = GetConnection();
const string query = @"
SELECT 
    bp.*
FROM BlogPost bp
WHERE EXISTS (SELECT 1
    FROM BlogPostTags tags
    WHERE tags.blogId = bp.Id
      AND tags.Id IN @tags
);

var results = await connection.QueryAsync<BlogPost>(query, new { tags, });

Для двух других требований это классическое Реляционное деление, одно — с остатком, другое — без остатка.

Для With Remainder просто выполните EXISTS объединение и сгруппированную HAVING проверку, что все они существуют.

var tagIds = new[]{ 1, 2, };

using connection = GetConnection();
const string query = @"
SELECT 
    bp.*
FROM BlogPost bp
WHERE EXISTS (SELECT 1
    FROM BlogPostTags tags
    WHERE tags.blogId = bp.Id
      AND tags.Id IN @tags
    HAVING COUNT(*) = @count   -- are all input matched?
);

var results = await connection.QueryAsync<BlogPost>(query, new { tags, count = tags.Length });

Для «Без остатка» это аналогично, но вам нужно найти все теги, а затем проверить HAVING, что совпадают только те, которые вы ищете.

var tagIds = new[]{ 1, 2, };

using connection = GetConnection();
const string query = @"
SELECT 
    bp.*
FROM BlogPost bp
WHERE EXISTS (SELECT 1
    FROM BlogPostTags tags
    WHERE tags.blogId = bp.Id
    HAVING COUNT(*) = @count   -- are all input matched?
       AND COUNT(*) = COUNT(CASE WHEN tags.Id IN @tags THEN 1 END)   -- are all tags in the list
);

var results = await connection.QueryAsync<BlogPost>(query, new { tags, count = tags.Length });

Обратите внимание, что Dapper автоматически параметризирует списки как (@p1, @p2) и т. д.


Вы также можете использовать параметр с табличным значением, который более эффективен для больших списков.

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

CREATE TYPE dbo.IntList AS TABLE (value int PRIMARY KEY);

Затем поместите значения в DataTable и используйте .AsTableValuedParameter.

Запрос также немного отличается, поскольку здесь вы бы использовали соединение.

var tagIds = new[]{ 1, 2, };
var table = new DataTable { Columns = { { "value", typeof(int) } } };
foreach (var tag in tagIds)
    table.Add(tag);

using connection = GetConnection();
const string query = @"
SELECT 
    bp.*
FROM BlogPost bp
WHERE EXISTS (SELECT 1
    FROM BlogPostTags tags
    LEFT JOIN @tags input ON input.value = tags.Id
    WHERE tags.blogId = bp.Id
    HAVING COUNT(*) = @count   -- are all input matched?
       AND COUNT(*) = COUNT(input.value)   -- did the join match all tags?
);

var results = await connection.QueryAsync<BlogPost>(query, new { tags = tags.AsTableValuedParameter("dbo.IntList"), count = tags.Length });

Большое спасибо - я подумал, что для этого есть концепция - просто не знал о ней здесь. Я попробую поиграться с вашими примерами и посмотреть, смогу ли я что-нибудь получить.

user3116167 22.02.2024 14:37

Другие вопросы по теме