2

複数テーブルの全文ブール検索に一意のキーを追加すると、結果は 3 つの任意の状態のうちの 1 つを循環し、1 つだけが正しい状態になります。

以下の sqlfiddle をチェックするときは、これを念頭に置いてください。クエリは最初は正しく機能する可能性があるためです。そのような場合は、左側のパネルに空白を追加してから再構築して再実行します。その後、クエリは壊れているはずです (ただし、非常にヒットアンドミスです)。

http://sqlfiddle.com/#!9/8d95ba/18

問題のクエリは次のとおりです。

SELECT `i`.`item_id`, `g_a`.`alias` AS `group`, `i`.`name` AS `name`
  FROM `item` `i`
  JOIN `group_alias` `g_a` USING (group_id)
    WHERE
      MATCH (`g_a`.`alias`) AGAINST ('Mac*' IN BOOLEAN MODE)
    OR
      MATCH (`i`.`name`) AGAINST ('Mac*' IN BOOLEAN MODE);

十分に単純です。ただし、次の一意のインデックスが追加されています。

ALTER TABLE `item_with_unique` ADD UNIQUE INDEX `unique_item_group` (`group_id`, `name`)

結果は、次の 3 つの状態の間を任意に循環します。

  1. WHERE 句がないかのようにすべての行が返されます
  2. エイリアスの一致は、WHERE 句に OR 部分がないかのように返されます。
  3. 正しい結果が返されます (私の経験では、これが最もまれでした)

動作は、クエリが何らかのマイナーな方法で変更される (大括弧を追加するなど) か、スキーマが再構築されるまで、これら 3 つの状態のいずれかに一貫しているように見えます。その時点で、変更される可能性があります。

これらは、この動作を説明している MySQL ドキュメントで見落としていた何らかの制限ですか? バグですか?それとも、明らかに間違ったことをしただけですか?

Mysql バージョン 5.6.35 (執筆時点では sqlfiddle)。

リンクが切れた場合の子孫のための Sqlfiddle:

CREATE TABLE `group` (
  `group_id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `name` VARCHAR(256),
  FULLTEXT INDEX `search` (`name`)
) ENGINE = InnoDB;

CREATE TABLE `group_alias` (
  `group_id` INT UNSIGNED NOT NULL,
  `alias` VARCHAR(256),
  CONSTRAINT `alias_group_id`
    FOREIGN KEY (`group_id`)
    REFERENCES `group` (`group_id`),
  FULLTEXT INDEX `search` (`alias`)
) ENGINE = InnoDB;

CREATE TABLE `item` (
  `item_id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `group_id` INT UNSIGNED,
  `name` VARCHAR(255) NOT NULL,
  CONSTRAINT `item_group_id`
    FOREIGN KEY (`group_id`)
    REFERENCES `group` (`group_id`),
  FULLTEXT INDEX `search` (`name`)
) ENGINE = InnoDB;

CREATE TABLE `item_with_unique` LIKE `item`;
ALTER TABLE `item_with_unique` ADD UNIQUE INDEX `unique_item_group` (`group_id`, `name`);

INSERT INTO `group` (`group_id`, `name`) VALUES (1, 'Thompson');
INSERT INTO `group` (`group_id`, `name`) VALUES (2, 'MacDonald');
INSERT INTO `group` (`group_id`, `name`) VALUES (3, 'Stewart');

INSERT INTO `group_alias` (`group_id`, `alias`) VALUES (1, 'Tomson');
INSERT INTO `group_alias` (`group_id`, `alias`) VALUES (2, 'Something');
INSERT INTO `group_alias` (`group_id`, `alias`) VALUES (3, 'MacStewart');

INSERT INTO `item` (`item_id`, `group_id`, `name`) VALUES (1, 1, 'MacTavish');
INSERT INTO `item` (`item_id`, `group_id`, `name`) VALUES (2, 1, 'MacTavish; Red');
INSERT INTO `item` (`item_id`, `group_id`, `name`) VALUES (3, 2, 'MacAgnew');
INSERT INTO `item` (`item_id`, `group_id`, `name`) VALUES (4, 3, 'Spider');
INSERT INTO `item` (`item_id`, `group_id`, `name`) VALUES (5, 2, 'blahblah');

INSERT INTO `item_with_unique` SELECT * FROM `item`;


SELECT `i`.`item_id`, `g_a`.`alias` AS `group`, `i`.`name` AS `name`,
IF(MATCH (`g_a`.`alias`) AGAINST ('Mac*' IN BOOLEAN MODE), 1, 0) AS `group_match`,
IF(MATCH (`i`.`name`) AGAINST ('Mac*' IN BOOLEAN MODE), 1, 0) AS `item_match`
  FROM `item` `i`
  JOIN `group_alias` `g_a` USING (group_id)
    WHERE
      MATCH (`g_a`.`alias`) AGAINST ('Mac*' IN BOOLEAN MODE)
    OR
      MATCH (`i`.`name`) AGAINST ('Mac*' IN BOOLEAN MODE);

SELECT "Same query, using table with unique index (NOTE: sporadically this is actually correct, in such case, skip to bottom notes)";
SELECT `i`.`item_id`, `g_a`.`alias` AS `group`, `i`.`name` AS `name`,
IF(MATCH (`g_a`.`alias`) AGAINST ('Mac*' IN BOOLEAN MODE), 1, 0) AS `group_match`,
IF(MATCH (`i`.`name`) AGAINST ('Mac*' IN BOOLEAN MODE), 1, 0) AS `item_match`
  FROM `item_with_unique` `i`
  JOIN `group_alias` `g_a` USING (group_id)
    WHERE
      MATCH (`g_a`.`alias`) AGAINST ('Mac*' IN BOOLEAN MODE)
    OR
      MATCH (`i`.`name`) AGAINST ('Mac*' IN BOOLEAN MODE);

SELECT "Union of the two OR match conditions seperately (expected result from second query)";
SELECT `i`.`item_id`, `g_a`.`alias` AS `group`, `i`.`name` AS `name`,
IF(MATCH (`g_a`.`alias`) AGAINST ('Mac*' IN BOOLEAN MODE), 1, 0) AS `group_match`,
IF(MATCH (`i`.`name`) AGAINST ('Mac*' IN BOOLEAN MODE), 1, 0) AS `item_match`
  FROM `item_with_unique` `i`
  JOIN `group_alias` `g_a` USING (group_id)
    WHERE
      MATCH (`g_a`.`alias`) AGAINST ('Mac*' IN BOOLEAN MODE)
UNION
SELECT `i`.`item_id`, `g_a`.`alias` AS `group`, `i`.`name` AS `name`,
IF(MATCH (`g_a`.`alias`) AGAINST ('Mac*' IN BOOLEAN MODE), 1, 0) AS `group_match`,
IF(MATCH (`i`.`name`) AGAINST ('Mac*' IN BOOLEAN MODE), 1, 0) AS `item_match`
  FROM `item_with_unique` `i`
  JOIN `group_alias` `g_a` USING (group_id)
    WHERE
      MATCH (`i`.`name`) AGAINST ('Mac*' IN BOOLEAN MODE);

SELECT "Now rebuild the schema (add a newline somewhere so sqlfiddle thinks it has changed) and observe that the results of the second query.  It may take multiple attempts but it usually cycles between 3 states:";
SELECT "1: Returns ALL results as if there were no conditions (5 rows)";
SELECT "2: Returns results as if there were no second part to the OR condition (1 row)";
SELECT "3: Returns the correct results (rarely)";
4

2 に答える 2

1

IGNORE INDEXあなたのステートメントに使用してみてください:

SELECT `i`.`item_id`, `g_a`.`alias` AS `group`, `i`.`name` AS `name`
  FROM `item` `i`
  IGNORE INDEX (unique_item_group)
  JOIN `group_alias` `g_a` USING (group_id)
    WHERE
      MATCH (`g_a`.`alias`) AGAINST ('Mac*' IN BOOLEAN MODE)
    OR
      MATCH (`i`.`name`) AGAINST ('Mac*' IN BOOLEAN MODE);

unique_item_groupMySQL は、全文検索にも無作為に使用するのは非常に愚かです。

于 2019-10-28T13:33:27.010 に答える