仅获取在另一个表中具有(最多)1列值的行

时间:2017-08-07 19:22:17

标签: mysql sql

我有一个在PHP中动态构建的查询,它使用一组连接在一起的子查询来选择对象值的不同组合...例如,给定值表:

[object_id]    [value_id]
99             1
99             2
99             3

我会得到以下结果:

[object_id]  [a0]   [a1]    [a2]
99           null   null    null  // empty is a valid combination
99           1      null    null
99           null   2       null
99           null   null    3
99           1      2       null
99           null   2       3
99           1      2       3

我有一个单独的表,列出了这些对象值的无效组合:

INVALID_VALUES

[object_id] [value]
  99         1
  99         2

我正在尝试使用无效值列表来过滤第一个查询的结果。更具体地说,如果2个或更多 a*列作为invalid_values中的值存在,则不包括该行。因此,使用此示例,结果中不会包含1 2 null 991 2 3 99,因为1& 2在记录中并且是无效值。

我有一个查询能够满足我的需求,但我担心它效率很低。对于我正在构建的每个子查询,我加入另一个子查询,该子查询选择逗号分隔的无效值。我用它来表明值列是否是“不兼容的候选者”。在所有这些结束时,我将所有不兼容的候选列加起来,如果它是2或更多,则它们不会被返回。以下是为“object”id 99:

构建的查询示例
SELECT a0.value_id AS '0', a1.value_id AS '1', a2.value_id AS '2'
FROM (
    SELECT NULL AS value_id, 99 AS object_id, 0 AS incompatible_candidate
    UNION ALL
    SELECT value.value_id, value.object_id, CASE WHEN FIND_IN_SET(value.value_id, pivs.valueIds) THEN 1 ELSE 0 END AS incompatible_candidate
    FROM value
    JOIN (
        SELECT GROUP_CONCAT(iv.value_id ORDER BY iv.value_id ASC) AS valueIds
        FROM invalid_values iv
        WHERE iv.object_id = 99
        GROUP BY iv.object_id
    ) pivs
    WHERE value.value_id IN (1) AND value.object_id = 99
) AS a0
JOIN (
    SELECT NULL AS value_id, 99 AS object_id, 0 AS incompatible_candidate
    UNION ALL
    SELECT value.value_id, value.object_id, CASE WHEN FIND_IN_SET(value.value_id, pivs.valueIds) THEN 1 ELSE 0 END AS incompatible_candidate
    FROM value
    JOIN (
        SELECT GROUP_CONCAT(iv.value_id ORDER BY iv.value_id ASC) AS valueIds
        FROM invalid_values iv
        WHERE iv.object_id = 99
        GROUP BY iv.object_id
    ) pivs
    WHERE value.value_id IN (2) AND value.object_id = 99
) AS a1
JOIN (
    SELECT NULL AS value_id, 99 AS object_id, 0 AS incompatible_candidate
    UNION ALL
    SELECT value.value_id, value.object_id, CASE WHEN FIND_IN_SET(value.value_id, pivs.valueIds) THEN 1 ELSE 0 END AS incompatible_candidate
    FROM value
    JOIN (
        SELECT GROUP_CONCAT(iv.value_id ORDER BY iv.value_id ASC) AS valueIds
        FROM invalid_values iv
        WHERE iv.object_id = 99
        GROUP BY iv.object_id
    ) pivs
    WHERE value.value_id IN (3) AND value.object_id = 99
) AS a2
GROUP BY a0.value_id, a1.value_id, a2.value_id
HAVING SUM(a0.incompatible_candidate + a1.incompatible_candidate + a2.incompatible_candidate) < 2;

有人可以帮我构建更有效的查询吗?

1 个答案:

答案 0 :(得分:1)

使用排列表检测无效行,即具有两个或更多无效值的行。您可以更改为有效行的< 2

<强> SQL DEMO

SELECT * 
FROM Permutation P
LEFT JOIN Invalid  x
  ON (P.`a0`, P.`object_id`) = (x.`value_id`, x.`object_id`)
LEFT JOIN Invalid  y
  ON (P.`a1`, P.`object_id`) = (y.`value_id`, y.`object_id`)
LEFT JOIN Invalid  z
  ON (P.`a2`, P.`object_id`) = (z.`value_id`, z.`object_id`)
 AND P.`object_id` = z.`object_id`
WHERE CASE WHEN x.`value_id` IS NULL THEN 0 ELSE 1 END + 
      CASE WHEN y.`value_id` IS NULL THEN 0 ELSE 1 END + 
      CASE WHEN z.`value_id` IS NULL THEN 0 ELSE 1 END 
      >= 2
;

<强>输出

enter image description here