我在编写查询以返回正确的数据时遇到了麻烦,而且我越来越不相信它甚至可以通过单个查询来实现。
我将日志记录存储在 MySQL 数据库中,其方式与 printf() 的工作方式非常相似,只是我必须将格式字符串与替换值分开存储。在搜索某些值的情况下,我想做的是以最有效的方式返回这些数据。
这是表设置:
CREATE TABLE `log` (
`log_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`message` varchar(255) NOT NULL,
`num_variables` int(10) unsigned NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`log_id`)
);
CREATE TABLE `variable` (
`log_id` int(10) unsigned NOT NULL,
`order` int(10) unsigned NOT NULL,
`name` varchar(255) NOT NULL,
`value_id` int(10) unsigned NOT NULL,
KEY `log_id` (`log_id`),
KEY `value_id` (`value_id`)
);
CREATE TABLE `value` (
`value_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`value` varchar(255) NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`value_id`),
UNIQUE KEY `value` (`value`)
);
这是一个示例用法:
log('user %email% invited %num% new players', 'him@example.com', 2);
这将导致以下查询:
-- create the log record (resulting PK would be 1)
INSERT INTO log
(message, num_variables)
VALUES
('user %email% invited %num% new players', 'him@example.com', '2');
-- create the first value record (resulting PK would be 1)
INSERT INTO value
(value)
VALUES
('him@example.com');
-- create the first variable record (resulting PK would be 1)
INSERT INTO variable
(log_id, order, name, value_id)
VALUES
(1, 0, 'email', 1);
-- create the second value record (resulting PK would be 2)
INSERT INTO value
(value)
VALUES
('2');
-- create the second variable record (resulting PK would be 2)
INSERT INTO variable
(log_id, order, name, value_id)
VALUES
(1, 1, 'num', 2);
现在我希望能够从数据库中提取日志记录,以及它们相关的变量和值。具体来说,我需要日志消息,以及它的所有关联值:
SELECT log.id, log.message
variable.order, variable.name
value.value_id, value.value
FROM log
LEFT JOIN variable ON (log.log_id = variable.log_id)
LEFT JOIN value ON (variable.value_id = value.value_id)
如果我想要所有日志记录,这很好用(忽略 log.log_id 和 log.message 对于任何具有多个变量的日志都会冗余返回的事实)。但我想要更多的特异性。
借用上面的例子,我希望能够指定我只想要包含“him@example.com”的“电子邮件”的日志记录,比方说。当我将其添加到我的查询中时...
SELECT log.log_id, log.message
variable.order, variable.name
value.value_id, value.value
FROM log
LEFT JOIN variable ON (log.log_id = variable.log_id)
LEFT JOIN value ON (variable.value_id = value.value_id)
WHERE (variable.name = 'email' AND value.value = 'him@example.com')
它将返回该日志/变量/值记录,但不会返回关联的“num = 2”记录(这是完全重建日志所必需的)。此外,假设我想指定第二个约束,比如“action”=“logged out”。我可以(错误地)将我的 WHERE 子句更改为如下所示:
-- won't return anything
WHERE (variable.name = 'email' AND value.value = 'him@example.com')
AND (variable.name = 'action' AND value.value = 'logged out')
或这个:
-- will also return logs containing only ONE of the given constraints
WHERE (variable.name = 'email' AND value.value = 'him@example.com')
OR (variable.name = 'action' AND value.value = 'logged out')
但在任何一种情况下,您都可以看到它错过了标记,并且没有返回我正在寻找的确切结果集。
我的桌子是否设计不佳(或不足或过度)?我是否以错误的方式接近查询?将派生数据字段存储在某处能给我我需要的东西吗?是否有一些我未能使用的 JOIN 可以解决问题?
更新 1:
variable.order 和 variable.name 只是确保将值正确插回 log.message 的两种不同方法。
更新 2:
根据评论,值得注意的是,这些表是简化帖子的人为示例 - 实际的表结构比呈现的稍微复杂一些。我只是将这种复杂性降低到问题的核心。简单的 use-a-single-table-and-serialize-the-value 技术对我不起作用。除此之外,我们需要能够非常快速地根据值查找这些日志,而这样的解决方案不会为我们提供适当的索引功能。