Sae*_*eid 6 mysql optimization full-text-search
我需要根据客户输入选择带有optional columns
inwhere
子句的数据。我的查询是这样的:
SELECT a.id, a.title, b.txt AS b_txt, c.txt AS c_txt
FROM a
LEFT JOIN b ON a.b_id=b.id
LEFT JOIN c ON a.c_id=c.id
where a.status=1
AND (0=@bid OR a.b_id=@bid)
AND (0=@cid OR a.c_id=@cid)
Run Code Online (Sandbox Code Playgroud)
@bid
并且@cid
是客户端输入,如果客户端不提供输入,则这些值必须>0
使用默认值0
。
客户端可以不提供任何输入,因此它将选择status=1
忽略b_id
和c_id
列的所有数据
客户可以提供@bid
或提供@cid
两者,查询将相应地选择。
表引擎是 InnoDB,列有索引,外键和关系集。
到目前为止一切都很好。EXPLAIN SQL
显示选择是根据提供的索引完成的。
现在我需要在所有 3 个表上添加全文搜索来查询是我遇到问题的地方。如果客户端提供任何关键字进行搜索,文本搜索也是可选的。
全文索引定义为 a.title、b.txt 和 c.txt
我将查询更改为:
SELECT a.id, a.title, b.txt AS b_txt, c.txt AS c_txt
FROM a
LEFT JOIN b ON a.b_id=b.id
LEFT JOIN c ON a.c_id=c.id
where a.status=1
AND ('0'=@keywords OR (MATCH(a.title) AGAINST(@keywords IN BOOLEAN MODE) OR MATCH(b.txt) AGAINST(@keywords IN BOOLEAN MODE) OR MATCH(c.txt) AGAINST(@keywords IN BOOLEAN MODE)))
AND (0=@bid OR a.b_id=@bid)
AND (0=@cid OR a.c_id=@cid)
Run Code Online (Sandbox Code Playgroud)
查询似乎正在返回我想要的结果,但explain query
返回type=ALL
和全表扫描所以没有查询没有以优化的方式工作。
如果我将OR
匹配之间更改为AND
然后explain query
返回type=fulltext
并选择在 FULLTEXT 索引上完成,但我需要OR
样式。
我正在考虑加入不同的结果集,但无法找出如何加入,因为输入是可选的,并且可能没有输入,因此不需要进行全文搜索。
任何解决方案?
编辑:
好的,感谢 jkavalik 的评论和 Rick 的回答,看来我需要添加一些说明:
实际上我使用的是 WSO2 数据服务服务器的数据服务。所以我只是将输入参数传递给数据服务,我无法根据用户输入生成选择查询。(有一个选项可以将查询字符串的一部分作为输入参数传递,但出于安全原因我不打算这样做)
所以我有两个选择:
编写单独的查询,为每种输入参数组合情况接受不同的输入参数集。根据用户输入决定将参数发送到哪个查询。好吧,在实际情况下,可选输入是 10+,这将是很多查询,而且似乎不是正确的解决方案。可选字段可能会随着时间的推移而改变,未来的维护成本很高。看起来不是处理这个问题的标准方法。
以可以处理可选字段的方式编写单个查询。
我更喜欢选项 2,除非还有我不知道的第三个选项?
在查询被传递到 MySQL 并在 MySQL 中执行之前,@variables
它们要么被输入参数的值替换,要么被默认值(0
在本例中)替换。实际上它们:variable
不是,@variable
但我@
在本示例中将其更改为更好的阅读或理解。
错误的做法。 相反,SELECT
根据用户提供的字段“构建”。
为什么? OR
优化工作做得很差。相反,如果您从 中删除该子句WHERE
,则不需要测试 0,从而避免OR
.
更糟糕的是OR
跨越多个表,因为您需要进行检查FULLTEXT
。就目前而言,运行查询的唯一方法是评估所有表中所有行的所有组合,然后开始过滤。(好吧,我夸大了,但没有夸张太多。)
避免的常见方法OR
是将其变成UNION
:
( SELECT id FROM a WHERE MATCH(a...) ... )
UNION DISTINCT
( SELECT id FROM a JOIN b ON ... WHERE MATCH(b...) ... )
...
Run Code Online (Sandbox Code Playgroud)
这将获得 的所有可能值a.id
。
为什么LEFT
? 这会无缘无故地减慢子查询的第二部分和第三部分的速度。消除LEFT
。如果没有b
行,则相当于MATCH
失败。
然后,将其放入子查询中以获取其余所需数据:
SELECT a.id, a.title, b.txt AS b_txt, c.txt AS c_txt
FROM ( the above "union" query ) AS tmp
JOIN a
LEFT JOIN b ON a.b_id=b.id
LEFT JOIN c ON a.c_id=c.id;
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您可能需要LEFT
;但如果不这样做,请将其删除。
其他注意事项
当您“构造”查询时,您也可以插入@值。@变量在某些情况下会抑制优化器;实际的文字更容易处理。请务必转义它们以帮助防止“sql 注入”。
如果不明显,0=@bid OR
就消失。
归档时间: |
|
查看次数: |
1424 次 |
最近记录: |