【问题标题】:High performance SQL tag search query with logical operations具有逻辑运算的高性能 SQL 标签搜索查询
【发布时间】:2021-12-06 19:03:12
【问题描述】:

How can I implement a boolean tag search in SQL?
这个问题与我能找到的差不多,但有几个。

我知道的唯一真正的解决方案是通过后端代码生成这样的查询并将其放入 SQL,但我想它很慢,我也想知道是否有其他方法可以做到这一点(例如一个主查询而不是多个)。

还有可能使用IN 或类似的解决方案:
How to query data based on multiple 'tags' in SQL?

我无法使用典型的GROUP BY HAVING COUNT 解决方案,因为它无法在具有标签列表的上下文中运行,正如该用户指出的那样:Implementing a tag search with operands

我应该指定大多数现有解决方案都不起作用,因为我正在寻找能够进行更复杂查询的东西,例如括号分组和嵌套操作数。

架构是“毒药”http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/

SELECT id AS post_id
FROM posts
WHERE EXISTS (SELECT name FROM tags WHERE post IS post_id AND name IS 'random')
AND NOT (
    EXISTS (SELECT name FROM tags WHERE post IS post_id AND name IS 'query') AND
    EXISTS (SELECT name FROM tags WHERE post IS post_id AND name IS '1')
)
AND EXISTS (SELECT name FROM tags WHERE post IS post_id AND name IS '2') 
AND EXISTS (SELECT name FROM tags WHERE post IS post_id AND name IS '3')
AND EXISTS (SELECT name FROM tags WHERE post IS post_id AND name IS 'racecar')

【问题讨论】:

  • 你还有什么问题。您已经链接了几个问题,但我不明白您为什么不能使用它们。
  • @SalmanA 我正在尝试找到一种更快或一般替代方法来执行此操作,但其中一些根本不起作用。
  • 好吧,为了更快,您首先需要确定当前解决方案是否缓慢。不过,如果可以接受的话,我会发布一个替代方案。

标签: sql database many-to-many relational-division


【解决方案1】:

GROUP BY HAVING COUNT 可以使用,而且速度快、用途广泛。一些例子:

CREATE TABLE tags(
    post_id INT,
    name VARCHAR(50),
    UNIQUE KEY (post_id, name)
);

INSERT INTO tags(post_id, name) VALUES
(1, 'foo'),
(1, 'bar'),

(2, 'foo'),

(3, 'bar'),

(4, 'baz'),

(5, 'foo'),
(5, 'bar'),
(5, 'meh');

-- posts tagged foo AND bar
-- returns 1, 5
SELECT post_id
FROM tags
GROUP BY post_id
HAVING COUNT(CASE WHEN name IN ('foo', 'bar') THEN 1 END) = 2;

-- posts tagged foo OR bar
-- returns 1, 2, 3, 5
SELECT post_id
FROM tags
GROUP BY post_id
HAVING COUNT(CASE WHEN name IN ('foo', 'bar') THEN 1 END) > 0;

-- posts tagged (foo AND bar) OR (baz)
-- returns 1, 4, 5
SELECT post_id
FROM tags
GROUP BY post_id
HAVING COUNT(CASE WHEN name IN ('foo', 'bar') THEN 1 END) = 2
OR     COUNT(CASE WHEN name IN ('baz') THEN 1 END) = 1;

-- posts tagged (foo AND bar) AND (no other tags)
-- returns 1
SELECT post_id
FROM tags
GROUP BY post_id
HAVING COUNT(CASE WHEN name IN ('foo', 'bar') THEN 1 END) = 2
AND    COUNT(*) = 2;

-- posts tagged (foo OR bar) AND NOT (meh)
-- returns 1, 2, 3
SELECT post_id
FROM tags
GROUP BY post_id
HAVING COUNT(CASE WHEN name IN ('foo', 'bar') THEN 1 END) > 0
AND    COUNT(CASE WHEN name IN ('meh') THEN 1 END) = 0;

Demo on DB<>Fiddle


将tag1 AND tag2 OR tag3 等表达式转换为对应的HAVING COUNT 不在我的回答中,但五个示例应该足够了。

【讨论】:

  • -- 帖子标记为 foo AND bar SELECT post_id, GROUP_CONCAT(name) AS for_testing FROM tags GROUP BY post_id HAVING COUNT(CASE WHEN name IN ('foo', 'bar') THEN 1 END) = 2 AND NOT (COUNT(CASE WHEN name IN ('tes') THEN 1 END) = 1 AND COUNT(CASE WHEN name IN ('bru') THEN 1 END) = 0)
  • INSERT INTO tags(post_id, name) VALUES (1, 'foo'), (1, 'bar'), (2, 'foo'), (2, 'bar'), ( 2, 'tes'), (2, 'bru') 这似乎也适用于分组,谢谢。
  • 是的,我忘记了那个案子。基本上AND COUNT(CASE WHEN name IN ('tes', 'bru') THEN 1 END) = 0 可用于排除包含 tes 或 bru 或两者兼有的帖子。我会编辑。
【解决方案2】:

架构准备

CREATE TABLE posts (
    ID INT PRIMARY KEY IDENTITY(1,1),
    subj nvarchar(50)
)
GO

CREATE TABLE tags (
    post INT,
    name nvarchar(50)
)
GO

数据准备

INSERT INTO posts (subj) VALUES ('post1')
INSERT INTO posts (subj) VALUES ('post2')
INSERT INTO posts (subj) VALUES ('post3')

INSERT INTO tags VALUES (1, 'food')
INSERT INTO tags VALUES (1, 'spicy')

INSERT INTO tags VALUES (2, 'spicy')
INSERT INTO tags VALUES (2, 'recipe')

INSERT INTO tags VALUES (3, 'food')
INSERT INTO tags VALUES (3, 'spicy')
INSERT INTO tags VALUES (3, 'sweet')

查询

;WITH Aggregated_Tags AS (
    SELECT
        post,
        STRING_AGG(name, ',') AS name
    FROM tags
    GROUP BY post
)
SELECT post
FROM Aggregated_Tags
WHERE 
    (name LIKE '%food%' AND name LIKE '%spicy%' AND name NOT LIKE '%sweet%')
    OR (name LIKE '%recipe%')
GROUP BY post

如果我理解正确,您正在寻找这样的东西。这里的关键是聚合每个帖子的标签,以消除生成多个选择查询。这个解决方案并不完整,但我相信这是一个好的开始。

【讨论】:

    猜你喜欢
    • 2012-01-10
    • 2016-09-10
    • 2022-11-03
    • 2015-05-22
    • 2011-01-29
    • 1970-01-01
    • 1970-01-01
    • 2016-08-28
    • 1970-01-01
    相关资源
    最近更新 更多