Mysql子字符串查询

时间:2019-05-13 07:49:32

标签: mysql sql mariadb

我有一个产品代码区域限制表。产品代码没有固定的长度,可能介于10到25个数字之间。该限制可以包含产品的前缀,以便禁止该范围内的所有产品。 使用的数据库是MariaDB / Mysql,这是表定义:

CREATE TABLE product_restrict (
    `id` VARCHAR(25) NOT NULL,
    `region` VARCHAR(3) NOT NULL,
    `from_dttm` DATETIME NOT NULL,
    `to_dttm` DATETIME NULL DEFAULT NULL,
    PRIMARY KEY (`region`, `id`, `from_dttm`))
ENGINE = InnoDB;

此刻,我使用了15个由目标产品代码的长度定义的查询,因此我能够找到所有前缀,因此在我的代码中,我有15个这样的查询:

SELECT * 
FROM product_restrict
WHERE
 region='XXX' AND
(
    id = "9" OR 
    id = "98" OR 
    id = "987" OR 
    id = "9876" OR 
    id = "98765" OR 
    id = "987654" OR 
    id = "9876543" OR 
    id = "98765432" OR 
    id = "987654321" OR 
    id = "9876543210" OR 
    id = "98765432109" OR 
    id = "987654321098" OR 
    id = "9876543210987" OR 
    id = "98765432109876" OR 
    id = "987654321098765" OR 
    id = "9876543210987654" 
) AND (
 now() >= from_dttm AND
 ( now() < to_dttm OR to_dttm is null)
);


SELECT * 
FROM product_restrict
WHERE
 region='XXX' AND
(
    id = "9" OR 
    id = "98" OR 
    id = "987" OR 
    id = "9876" OR 
    id = "98765" OR 
    id = "987654" OR 
    id = "9876543" OR 
    id = "98765432" OR 
    id = "987654321" OR 
    id = "9876543210" OR 
    id = "98765432109" OR 
    id = "987654321098" OR 
    id = "9876543210987" OR 
    id = "98765432109876" OR 
    id = "987654321098765" OR 
    id = "9876543210987654" OR 
    id = "98765432109876543" 
) AND (
 now() >= from_dttm AND
 ( now() < to_dttm OR to_dttm is null)
);

在此表中,大约有1亿条记录。我的问题是,有没有办法将其简化为具有相同选择性能的单个查询?不幸的是,更改表结构是我的能力。


在@Pham X的INSTR()提示之后进行编辑。Bach:

我在本地样本数据库中进行了一些测试,该样本数据库中只有670 000个样本记录,并且INSTR()正在工作,但是从性能的角度看,它看起来要差得多。我必须等到明天,才能对生产样本进行此测试。

这里是分析(解释)我的原始查询:

Id  select_type table               type    posible_keys    key     key_len ref     rows    r_rows      filtered    r_filtered  Extra
1   SIMPLE      product_restrict    range   PRIMARY         PRIMARY 201             17      2.00        76.47       100.00      Using where

这里是INSTR:

Id  select_type table               type    posible_keys    key     key_len ref     rows    r_rows      filtered    r_filtered  Extra
1   SIMPLE      product_restrict    ref     PRIMARY         PRIMARY 98      const   335022  671732.00   100.00      0.00        Using where

INSTR查询,例如:

SELECT * 
FROM product_restrict
WHERE
 region='XXX' AND
 INSTR('98765432109876543', id) = 1 AND (
 now() >= from_dttm AND
 ( now() < to_dttm OR to_dttm is null)
);

3 个答案:

答案 0 :(得分:1)

首先,没有理由不能仅仅调整当前查询:

SELECT pr.* 
FROM product_restrict pr
WHERE pr.region = 'XXX' AND
      now() >= pr.from_dttm AND
      ( now() < pr.to_dttm OR pr.to_dttm is null) AND
      pr.id in ('9', '98', . . ., '98765432109876544',
                '9', '98', . . ., '98765432109876543'
               . . .
              )

具有{数百个条目的IN列表应该没有问题。重复的条目可以,但是您也可以将其删除。

您可以使用like或正则表达式更简单地编写此代码。例如:

WHERE pr.region = 'XXX' AND
      now() >= pr.from_dttm AND
      ( now() < pr.to_dttm OR pr.to_dttm is null) AND
      ('98765432109876544' LIKE concat(pr.id, '%') OR
       '98765432109876543' LIKE concat(pr.id, '%') OR
       . . .
      )

但是,IN的表现可能更好。

答案 1 :(得分:0)

您可以使用一个简单的类似条件

SELECT * 
FROM product_restrict
WHERE  region='XXX' 
AND '9876543210987654' like concat(id, '%')
AND (
      now() >= from_dttm 
      AND ( 
         now() < to_dttm OR to_dttm is null
      )
) ;

答案 2 :(得分:0)

另一种检查前缀的方法。

AND LEFT('9876543210987654', LENGTH(id)) = id

设计避免表扫描,检查所有ID的方法可能是不可能的。