1

正しいデータを返すクエリを作成するのに苦労しており、単一のクエリでそれが可能であるかどうかさえ確信が持てなくなっています。

printf() が機能するのとほとんど同じ方法で、MySQL データベースにログ レコードを保存していますが、書式文字列を置換値とは別に保存する必要がある点が異なります。私がやりたいのは、特定の値を検索した場合に、可能な限り最も効率的な方法でこのデータを返すことです。

テーブルのセットアップは次のとおりです。

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" の "email" を含むログ レコードのみが必要であると指定できるようにしたいと考えています。それをクエリに追加すると...

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" レコード (ログを完全に再構築するために必要) は返されません。さらに、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 つの異なる方法です。

更新 2:

コメントに基づいて、これらのテーブルは投稿を単純化するために考案された例であることに注意してください。実際のテーブル構造は、提示されているよりもわずかに複雑です。その複雑さを問題の核心にまで落としただけです。単一のテーブルを使用して値をシリアル化するという単純な手法は、私にはうまくいきません。それとは別に、値に基づいてこれらのログを非常に迅速に検索できる必要があり、そのようなソリューションでは適切なインデックス作成機能が提供されません。

4

2 に答える 2

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 に答える
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 対多の関係でない限り、変数テーブルと値テーブルを分離することに疑問を持ちます。

于 2012-09-13T20:08:37.967 に答える