我已经构建了一个UI小部件,它允许我创建一组嵌套规则。例如,我可以指定以下规则:从嵌套规则生成器生成MySQL查询
Match ALL of these rules
- Document Status == Open
- Has Tag = 'sales'
- Has Tag = 'question'
- Match ANY of these rules
- Has Tag = 'important'
- Has Tag = 'high-priority'
- Has Tag = 'critical-priority'
在英语中,这将转化这个查询:
Find Documents where status = Open AND has tag 'sales' AND has tag 'question'
AND has at least one of these tags: 'important', 'high-priority', 'critical-priority'
表结构类似于此。
Documents {id, title, status}
Tags {document_id, tag_value}
现在,在这一点上,我需要将这套规则转换为SQL查询。使用子查询可以很容易地完成,但是由于性能原因,Id宁愿避免它们。文档和标签表可能包含数百万条记录。
SELECT
d.id
FROM
Documents d
WHERE
d.status = 'open'
AND EXISTS (SELECT * FROM Tags t WHERE t.doc_id = d.id AND t.value = 'sales')
AND EXISTS (SELECT * FROM Tags t WHERE t.doc_id = d.id AND t.value = 'question')
AND (
EXISTS (SELECT * FROM Tags t WHERE t.doc_id = d.id AND t.value = 'important')
OR EXISTS (SELECT * FROM Tags t WHERE t.doc_id = d.id AND t.value = 'high-priority')
OR EXISTS (SELECT * FROM Tags t WHERE t.doc_id = d.id AND t.value = 'critical-priority')
)
如何重写此查询以使用更高效的联接?
我可以将前两个标记规则添加为INNER连接,但是如何处理规则集的后面部分?如果有进一步的规则需要标签出现以便文档出现,会怎么样?
请记住,可以将规则集设置为匹配ALL或其中的任意规则,并且它理论上可以嵌套多次。
关于解决此问题的一般方向的任何想法?
更新:
我优化我的表,发现查询似乎非常快(除了计数的匹配记录的数量,这是另一个问题)表的方法。我永远不会一次选择超过100个文档,并且具有约600k和200万个标签的文档集,这个解决方案将结果返回到0.02s,这比以前好得多。
有问题的表...
CREATE TABLE `app_documents` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`account_id` int(11) NOT NULL,
`status_id` int(11) DEFAULT NULL,
`subject` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_B91B1DB99B6B5FBA` (`account_id`),
KEY `IDX_B91B1DB96BF700BD` (`status_id`),
KEY `created_idx` (`created`),
KEY `updated_idx` (`updated`),
CONSTRAINT `FK_B91B1DB96BF700BD` FOREIGN KEY (`status_id`) REFERENCES `app_statuses` (`id`),
CONSTRAINT `FK_B91B1DB99B6B5FBA` FOREIGN KEY (`account_id`) REFERENCES `app_accounts` (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=500001 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `app_tags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`value` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `value_idx` (`value`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `app_documents_tags` (
`document_id` int(11) NOT NULL,
`tag_id` int(11) NOT NULL,
PRIMARY KEY (`document_id`,`tag_id`),
KEY `IDX_A849587A700047D2` (`document_id`),
KEY `IDX_A849587ABAD26311` (`tag_id`),
CONSTRAINT `FK_A849587ABAD26311` FOREIGN KEY (`tag_id`) REFERENCES `app_tags` (`id`) ON DELETE CASCADE,
CONSTRAINT `FK_A849587A700047D2` FOREIGN KEY (`document_id`) REFERENCES `app_documents` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
而且我对测试查询...
此查询查找所有文件和他们的标签有两个标签“蓝色”和“绿色“但不是”红色“。
SELECT
d.*
FROM
app_documents d
LEFT JOIN
app_documents_tags dtg ON ttg.document_id = d.id
LEFT JOIN
app_tags tg ON tg.id = dtg.tag_id
WHERE
d.account_id = 1
AND EXISTS (
SELECT
*
FROM
app_tags t1
CROSS JOIN
app_tags t2
CROSS JOIN
app_tags t3
INNER JOIN
app_documents_tags dtg1 ON t1.id = ttg1.tag_id
INNER JOIN
app_documents_tags dtg2 ON dtg1.ticket_id = dtg2.ticket_id AND dtg2.tag_id = t2.id
LEFT JOIN
app_documents_tags dtg3 ON dtg2.ticket_id = dtg3.ticket_id AND dtg3.tag_id = t3.id
WHERE
t1.value = 'blue' AND t2.value = 'green' AND t3.value = 'red' AND dtg3.ticket_id IS NULL AND dtg2.document_id = t.id
)
ORDER BY
d.created
LIMIT 45
我相信这可以通过使用更好的索引来改善。
数据库的架构或涉及的表的结构是什么? –
我将它列在原始问题中。与我正在使用的相比,它非常简化,但它足以显示我的意思。 –
@BartW如果你想提高性能,我们需要更多关于你的表格的信息,你可以做一个表演创建表格和'EXPLAIN QUERY'吗? – jcho360