我有一个产品代码区域限制表。产品代码没有固定的长度,可能介于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)
);
答案 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的方法可能是不可能的。