我正在尝试优化Akonadi中的SQL查询并遇到以下问题,这显然不容易用SQL解决,至少对我而言:
假设以下表结构(应该在SQLite,PostgreSQL,MySQL中工作):
CREATE TABLE a (
a_id INT PRIMARY KEY
);
INSERT INTO a (a_id) VALUES (1), (2), (3), (4);
CREATE TABLE b (
b_id INT PRIMARY KEY,
a_id INT,
name VARCHAR(255) NOT NULL
);
INSERT INTO b (b_id, a_id, name)
VALUES (1, 1, 'foo'), (2, 1, 'bar'), (3, 1, 'asdf'),
(4, 2, 'foo'), (5, 2, 'bar'), (6, 3, 'foo');
现在我的问题是在a
中找到表name
中缺少b
个条目的条目。例如。我需要确保a
中的每个条目至少包含表name
中的"foo"
个条目"bar"
和b
。因此查询应返回类似于:
a_id = 3 is missing name "bar"
a_id = 4 is missing name "foo" and "bar"
由于这两张桌子在Akonadi中可能都很大,因此表现至关重要。
MySQL中的一个解决方案是:
SELECT a.a_id,
CONCAT('|', GROUP_CONCAT(name ORDER BY NAME ASC SEPARATOR '|'), '|') as names
FROM a
LEFT JOIN b USING( a_id )
GROUP BY a.a_id
HAVING names IS NULL OR names NOT LIKE '%|bar|foo|%';
我还没有衡量明天的表现,但是对于a
中的数万个参赛作品和b
中的三十个参赛作品来说,它确实很快。此外,我们希望支持SQLite和PostgreSQL,据我所知,GROUP_CONCAT
函数不可用。
谢谢,晚安。
答案 0 :(得分:1)
这适用于任何SQL标准RDBMS:
SELECT
a.a_id,
Foo.b_id as Foo_Id,
Bar.b_id as Bar_Id
FROM a
LEFT OUTER JOIN (SELECT a_id, b_id FROM b WHERE name = 'foo') as Foo ON
a.a_id = Foo.a_id
LEFT OUTER JOIN (SELECT a_id, b_id FROM b WHERE name = 'bar') as Bar ON
a.a_id = Bar.a_id
WHERE
Foo.a_id IS NULL
OR Bar.a_id IS NULL
答案 1 :(得分:0)
那么,您可以在数据库中使用某些必需元素的定义。所以我会创建一个:
CREATE TABLE required(name varchar(255) primary key);
INSERT INTO required VALUES('foo'), ('bar');
(如果它是动态的,这可能是一个临时表或只是常量的内联联合)
现在我们期望在b中找到的行集由:
给出SELECT a.a_id, required.name FROM a CROSS JOIN required;
所以我们外部加入这个集合对b来确定什么是现在和什么不是:
SELECT a.a_id, required.name, b.b_id
FROM a
CROSS JOIN required
LEFT JOIN b ON b.a_id = a.a_id AND b.name = required.name;
或者:
SELECT a.a_id, required.name
FROM a CROSS JOIN required
WHERE NOT EXISTS (SELECT 1 FROM b WHERE b.a_id = a.a_id AND b.name = required.name);
假设在b(a_id,name)上有一个索引(并且你的描述似乎可能是唯一性约束)应该可以正常工作。在某种程度上,它将使用索引扫描和交叉检查b。
答案 2 :(得分:0)
我在Areen-Ugwu和Xgc的freenode上的#sql中得到了一个很好的提示:使用CrossTab模式:
SELECT a.a_id, SUM(name = "foo") as hasFoo, SUM(name = "bar") as hasBar, ...
FROM a
LEFT JOIN b USING (a_id)
GROUP BY a.a_id
HAVING hasFoo < 1 OR hasFoo IS NULL OR hasBar < 1 OR hasBar IS NULL...;
答案 3 :(得分:0)
事实证明,这些都不比简单地在程序本身中做那些东西更快......后者更容易做到,因此我毕竟选择了这个。