我在制作查询以返回正确的数据方面遇到了麻烦,而且我对单一查询甚至可能无法自信。
我有一个存储在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)
如果我想要 ALL 日志记录(忽略了对于包含多个变量的任何日志,冗余地返回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')
它将返回该日志/变量/值记录,但它将 NOT 返回关联的“num = 2”记录(这是完全重建日志所必需的)。另外,假设我想指定第二个约束,例如,“action”=“已注销”。我可以(错误地)改变我的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:
基于评论,值得注意的是,这些表是一个简化帖子的人为例子 - 实际的表格结构比呈现的稍微复杂一些。我只是将复杂性降低到了问题的核心。简单的使用 - 单表和序列化值技术对我来说不起作用。除此之外,我们需要能够非常快速地根据值查找这些日志,而这样的解决方案无法为我们提供正确的索引功能。
答案 0 :(得分:1)
怎么样:
...
WHERE log.id IN (SELECT l.id
FROM log l
INNER JOIN variable v ON l.log_id = v.log_id
INNER JOIN value vv ON v.value_id = vv.value_id
WHERE v.name = 'email' and vv.value = 'him@example.com')
在不知道更大的数据样本的情况下,我无法真正评论表格设计。在这一点上,我确实问题是分离出变量和值表,除非这是一对多的关系变量 - >值。
答案 1 :(得分:1)
那么你可以提出以下结构
CREATE TABLE `logs` (
`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 (`id`)
);
CREATE TABLE `logs_values` (
`log_id` int(10) unsigned NOT NULL,
`value_id` int(10) unsigned NOT NULL
);
CREATE TABLE `value` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name_id` int(10) unsigned NOT NULL,
`value` varchar(255) NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `value` (`value`)
);
CREATE TABLE `names`(
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
要获取所有日志记录,请运行此查询
SELECT * FROM logs
LEFT JOIN logs_values ON logs_values.log_id = logs.id
LEFT JOIN value ON logs_values.value_id = value.id
LEFT JOIN names ON value.name_id = names.id;
获取具有指定值的所有日志记录
SELECT * FROM logs
LEFT JOIN logs_values ON logs_values.log_id = logs.id
LEFT JOIN value ON logs_values.value_id = value.id
LEFT JOIN names ON value.name_id = names.id
WHERE names.name = 'email' AND value.value = 'email@email.com';
结果
ID MESSAGE NUM_VARIABLES CREATED VALUE NAME
1 test 2 September, 13 2012 16:24:31-0400 email@email.com email
P.S。当然,您需要设置所需的索引以获得更好的性能