MYSQL:在 IN 条件下使用变量

Pel*_*ang 2 mysql stored-procedures

编辑] 下面的查询不是我遇到性能问题的实际查询。这只是一个简化的查询。因为我只想知道是否可以将多个结果存储到一个变量中并将该变量用于 IN 条件。

我在下面的一个过程中有这个查询,我想对其进行更改以进行优化。

SELECT * FROM request WHERE facility_id IN (SELECT facility_id FROM facility);
Run Code Online (Sandbox Code Playgroud)

我想做的是将子查询存储到一个变量中并在主查询中使用该变量。

SET @facilities = (SELECT facility_id FROM facility);

SELECT * FROM request WHERE facility_id IN (@facilities);
Run Code Online (Sandbox Code Playgroud)

但我有一个错误Subquery returns more than 1 row。我该怎么做?任何帮助表示赞赏。

我找到了一些建议这样的链接http://www.mysqlperformancetuning.com/how-to-speed-up-mysql-by-optimization?nocache=1#comment-961。但我想不通。

And*_*y M 5

您可以使用GROUP_CONCAT聚合函数生成以逗号分隔的值列表:

SET @facilities = (SELECT GROUP_CONCAT(facility_id) FROM facility);
Run Code Online (Sandbox Code Playgroud)

然而,结果将只是一个字符串值。如果你像这样使用它:

SELECT * FROM request WHERE facility_id IN (@facilities);
Run Code Online (Sandbox Code Playgroud)

您不能期望在@facilities列表中的每个 ID 上都匹配。这是因为@facilities没有扩展为列表,而是被视为 IN 列表的单个项目。其中的逗号将被视为字符串值中的字符,而不是语法分隔符。

为了使用@facilities你想要的话,你就必须建立并执行动态查询围绕价值@facilities。它可能是这样的:

@stmt = CONCAT('SELECT * FROM request WHERE facility_id IN (', @facilities, ')');
PREPARE stmt FROM @stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Run Code Online (Sandbox Code Playgroud)

通过这种方式,查询不会包含一个参考@facilities-相反,这将有内容@facilities作为其组成部分,而逗号将因此被视为语法的一部分。

您要在其中使用@facilities列表的每个查询都必须以上述方式执行。我想你会同意这不是很方便。还有其他一些注意事项需要牢记。当您使用动态查询并将变量的内容连接到动态查询中时,您可能会受到SQL 注入攻击。此外,如果 IN 列表中有大量项目,性能可能会降低

我建议您使用临时表代替变量来存储 ID:

CREATE TEMPORARY TABLE tmp_facilities AS SELECT facility_id FROM facility;
Run Code Online (Sandbox Code Playgroud)

表格更加灵活,因为您不仅限于一种句法结构。尽管您仍然可以在 IN 谓词中使用临时表,如您的示例所示:

SELECT
  *
FROM
  request
WHERE
  facility_id IN (
    SELECT facility_id FROM tmp_facility
  )
;
Run Code Online (Sandbox Code Playgroud)

你也可以在 EXISTS 子查询中使用它:

SELECT
  *
FROM
  request AS r
WHERE
  EXISTS (
    SELECT * FROM tmp_facility AS f WHERE f.facility_id = r.facility_id
  )
Run Code Online (Sandbox Code Playgroud)

;

或在过滤连接中:

SELECT
  r.*
FROM
  request AS r
  INNER JOIN tmp_facilities AS f ON f.facility_id = r.facility_id
;
Run Code Online (Sandbox Code Playgroud)

当您需要调整查询以提高性能时,拥有许多如何编写查询的选项可能会有所帮助。