SQL查询将逗号分隔的字符串与逗号分隔的字符串匹配?

时间:2017-05-16 18:02:27

标签: php mysql

下面的MySQL查询使用PHP来引入$ sector,这是一个数字,$ subsector_text是逗号分隔的字符串。 $ subsector_text可以是单个数字或多个ID的列表,例如“3,4,7,9”。

  $sql = "
SELECT DISTINCT a.id
              , a.name
              , a.category_id
              , a.sector
              , a.subsector
              , a.year_in_operation
              , a.state
              , a.total_value
              , b.country_id
              , b.project_id
              , c.isocode_3
              , c.name
           FROM com_barchan_project a
           JOIN com_barchan_location b
             ON b.project_id = a.id
           JOIN com_barchan_country c
             ON c.id = b.country_id
           JOIN com_barchan_project_value_join d
             ON a.id = d.project_id
          WHERE a.state = 1 
            AND a.sector = '$sector'
            AND a.subsector REGEXP '^{$subsector_text}[,]|[,]{$subsector_text}[,]|[,]{$subsector_text}$|^{$subsector_text}$'
          ORDER 
             BY a.total_value DESC
              , a.category_id ASC
              , a.name ASC
";

我在上面的查询中遇到的问题是使用以下行:

AND a.subsector REGEXP '^{$subsector_text}[,]|[,]{$subsector_text}[,]|[,]{$subsector_text}$|^{$subsector_text}$'  

如果$ subsector_text =“3,4,5,9”,那么它只返回$ subsector字段中包含“3,4,5,9”的记录。

期望的结果是它将返回任何具有$ subsector_text中任何值的记录。例如,所有这些都应该返回,但目前不是。这个列表是一个例子,绝不是确切的。

1,3
1,5
1,3,7,9
3,5
3,4,5,9
9
3
5
4

如何更改查询以选择$ subsector_text字符串中包含值的任何记录?

请注意:如果$ subsector_text = 11,则不应选择以下内容。

1
12
21

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:2)

将逗号分隔字符串中的任何值与单个谓词中另一个以逗号分隔的字符串中的任何值进行匹配是不切实际的。

您可以使用FIND_IN_SET()一次搜索一个值。

这意味着您需要多个谓词,通过拆分输入$subsector_text,您可以获得每个值。因此,拆分变量并将其映射到一系列FIND_IN_SET()调用。

我没有测试过以下代码,但它应该让你知道我在说什么:

$subsector_array = array_map('intval', explode(',', $subsector_text));
$subsector_terms = array_map(
  function ($id) { return "FIND_IN_SET($id, a.subsector)"; },
  $subsector_array);
$subsector_expr = implode(' OR ', $subsector_terms);

$sql = "
SELECT ...
          WHERE a.state = 1 
            AND a.sector = '$sector'
            AND ($subsector_expr)
...";

这当然会强制进行表扫描,因为无法索引FIND_IN_SET()或搜索子字符串的任何其他操作。好吧,我认为a.statea.sector上的条件会在应用FIND_IN_SET()条件之前使用索引来缩小搜索范围。

我理解必须使用您继承的系统的困境。让你的经理知道这需要在某些时候进行重构,因为它现在的设计方式永远不会高效或可靠。

答案 1 :(得分:0)

您的方法是正确的,但需要进行一些修改。而不是只尝试在一个条件(REGEXP)中匹配,可以创建与OR ...

连接的多个条件

示例:

$subsectorArray = explode(',', $subsector_text);
$or = [];
foreach ($subsectorArray as $subsector){
    $or[] = "a.subsector REGEXP '[^[:alnum:]]{$subsector}[^[:alnum:]]|^{$subsector}[^[:alnum:]]|[^[:alnum:]]{$subsector}$|^{$subsector}$'";
}
$orStr = implode(' OR ', $or);

 $sql = "
SELECT DISTINCT a.id
              , a.name
              , a.category_id
              , a.sector
              , a.subsector
              , a.year_in_operation
              , a.state
              , a.total_value
              , b.country_id
              , b.project_id
              , c.isocode_3
              , c.name
           FROM com_barchan_project a
           JOIN com_barchan_location b
             ON b.project_id = a.id
           JOIN com_barchan_country c
             ON c.id = b.country_id
           JOIN com_barchan_project_value_join d
             ON a.id = d.project_id
          WHERE a.state = 1 
            AND a.sector = '$sector'
            AND ($orStr)
          ORDER 
             BY a.total_value DESC
              , a.category_id ASC
              , a.name ASC
";

答案 2 :(得分:0)

解决方案是重构应用程序。花了几天时间,但违规代码消失了,并创建了一个新的子部门表。感谢大家。