1

我在编写查询以返回正确的数据时遇到了麻烦,而且我越来越不相信它甚至可以通过单个查询来实现。

我将日志记录存储在 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 技术对我不起作用。除此之外,我们需要能够非常快速地根据值查找这些日志,而这样的解决方案不会为我们提供适当的索引功能。

4

2 回答 2

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')

在不知道更大的数据样本的情况下,我无法真正评论表格设计。在这一点上,我确实质疑将变量和值表分开,除非这是一个一对多的关系变量-> 值。

于 2012-09-13T20:08:37.967 回答
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

SQL小提琴

PS当然你需要设置所需的索引以获得更好的性能

于 2012-09-13T20:31:15.407 回答