【问题标题】:mySQL starts with in 2 directionsmySQL 从 2 个方向开始
【发布时间】:2016-02-12 13:03:50
【问题描述】:

我有下表

+-------+-------+-------+
|  ID   | Name1 | Name2 |
+-------+-------+-------+
|      1| Steve | Steven| - true
|      1| Steven| Steve | - true
|      1| Max   | Steve | - false
|      1| Steve | Steve | - true
+-------+-------+-------+

我需要确定行,其中Name1Name2 开头或Name2Name1 开头

问题:

有没有比Name2 LIKE Concat(Name1, '%') OR Name1 LIKE Concat(Name2, '%')更好的方法

这个例子有点简化,因为Name1 是一个巨大的子选择,像Concat(Name1, '%') LIKE Concat(Name2, '%') 这样的东西会大大提高我的应用程序的性能

更新:

指出我的问题,完整的选择看起来像

(SELECT Name FROM User JOIN UserGroup ON User.UserID = UserGroup.UserID JOIN Company ON Company.UserID = User.UserID WHERE UserGroup.Range > 2 AND User.Create > '2010-01-01' AND Company.Country = 'CH' ORDER BY User.StartDate DESC LIMIT 1) AS Name2

所以上面的语句看起来像

(SELECT Name FROM User JOIN UserGroup ON User.UserID = UserGroup.UserID JOIN Company ON Company.UserID = User.UserID WHERE UserGroup.Range > 2 AND User.Create > '2010-01-01' AND Company.Country = 'CH' ORDER BY User.StartDate DESC LIMIT 1) LIKE Concat(Name1, '%') OR Name1 LIKE Concat((SELECT Name FROM User JOIN UserGroup ON User.UserID = UserGroup.UserID JOIN Company ON Company.UserID = User.UserID WHERE UserGroup.Range > 2 AND User.Create > '2010-01-01' AND Company.Country = 'CH' ORDER BY User.StartDate DESC LIMIT 1), '%')

并且 select 被调用了两次 - 必须有一种方法可以从数据库中仅获取一次 Name2

【问题讨论】:

  • 你的方法很好。但是,您可能应该在 from 子句中进行复杂的计算。
  • 您可以尝试使用 LOCATE 代替,检查一个是否是另一个的子字符串,如果是,则出现的位置是 1 - 这至少可以消除 concact-with -百分号部分。 LOCATE(Name1, Name2) = 1 OR LOCATE(Name2, Name1) = 1
  • 在您更新的 SQL 中,Name1 来自哪里?
  • Name2 只是嵌套 select 语句的占位符,Name1 是上层 select 语句的字段
  • 抱歉,还没关注。编辑后的 ​​SQL 说:(big-select) LIKE Con​​cat(Name1,'%') OR Name1 LIKE Con​​cat(big-select, %)。 Name1 是另一个选择语句吗?这都是 WHERE 子句的一部分吗?

标签: mysql sql wildcard sql-like


【解决方案1】:

也许更快:

select *
from table1
WHERE substring(name2,1,length(name1))=name1

【讨论】:

  • 这不只是一种方式吗? SELECT SUBSTRING('Steve',1,LENGTH('Steven'))='Steven' 返回0
  • 你是对的:SELECT * from table1 WHERE (substring(name2,1,length(name1))=name1 or substring(name1,1,length(name2))=name2)
  • 好的,但情况变得更糟了——“名字”被调用了 3 次
  • 也许你可以使用“有”子句?
  • 从具有 (name1 REGEXP name2 或 name2 REGEXP name1) 的表中选择 (select ....) 作为 name1,(select ....) 作为 name2跨度>
【解决方案2】:

正如其他人评论的那样,您的方法很好。但是,有一种方法可以使用相等而不是 OR 和两个 LIKE 子句来编写它。想法是取两个名称的最小长度,将两个名称截断到该长度,然后比较它们。

select *, 
LEFT(Name1, LEAST(LENGTH(Name1), LENGTH(Name2))) =
LEFT(Name2, LEAST(LENGTH(Name1), LENGTH(Name2))) 
from Table1

这可能更有效,只是避免使用 OR 和 LIKE。虽然读起来比较麻烦,而且您说它需要与另一个复杂的表达式组合...要清理它,您可以将比较逻辑放入存储函数中。

** 更新 ** 我在您更新的示例中看到 Name2 实际上是一个嵌套的 SELECT,并且您宁愿不运行它 2 或 3 次。

您可以使用嵌套查询。内部查询构建了一个行表,看起来就像您的原始问题的简化示例。比如:

SELECT ID, Name1,
(SELECT Name FROM User JOIN UserGroup ON User.UserID = UserGroup.UserID JOIN Company  ON Company.UserID = User.UserID WHERE UserGroup.Range > 2 AND User.Create > '2010-01-01' AND Company.Country = 'CH' ORDER BY User.StartDate DESC LIMIT 1) AS Name2
FROM Table1

现在将这个“表”命名为PotentialMatch,并用一个外部查询包装它,该查询选择匹配的行。比如:

SELECT PotentialMatch.ID, PotentialMatch.Name1 FROM 
(SELECT ID, Name1,
  (SELECT Name FROM User JOIN UserGroup ON User.UserID = UserGroup.UserID JOIN Company  ON Company.UserID = User.UserID WHERE UserGroup.Range > 2 AND User.Create > '2010-01-01' AND Company.Country = 'CH' ORDER BY User.StartDate DESC LIMIT 1) AS Name2
FROM Table1) PotentialMatch
WHERE LEFT(PotentialMatch.Name1, LEAST(LENGTH(PotentialMatch.Name1), LENGTH(PotentialMatch.Name2))) =
LEFT(PotentialMatch.Name2, LEAST(LENGTH(PotentialMatch.Name1), LENGTH(PotentialMatch.Name2)))

或者,如果您愿意,也可以将此子查询技术与您原来的 LIKE 比较一起使用:

SELECT PotentialMatch.ID, PotentialMatch.Name1 FROM 
(SELECT ID, Name1,
  (SELECT Name FROM User JOIN UserGroup ON User.UserID = UserGroup.UserID JOIN Company  ON Company.UserID = User.UserID WHERE UserGroup.Range > 2 AND User.Create > '2010-01-01' AND Company.Country = 'CH' ORDER BY User.StartDate DESC LIMIT 1) AS Name2
FROM Table1) PotentialMatch
WHERE PotentialMatch.Name1 LIKE CONCAT(PotentialMatch.Name2, '%') 
OR PotentialMatch.Name2 LIKE CONCAT(PotentialMatch.Name1, '%')

没有 SQLFiddle 我无法测试这些,但希望你能明白。

【讨论】:

  • 在您的示例中,我必须选择 Name1 3 次而不是 2 次​​span>
  • 是的,但是重复引用一行中的列并不昂贵。在 WHERE 子句中使用 OR 可能会很昂贵,因为它使数据库更难有效地使用索引; OR 有时会强制它遍历多个行。而且 LIKE 是一个强大的通用字符串解析函数,它不会像截断字符串和执行单向比较那样高效......
  • 我的 cmets 是理论上的;当应用于您的问题时,差异可能可以忽略不计。我会为此添加一个注释。
  • 我从您的更新中了解到您为什么要消除对其中一列的多次引用。我试图在我的回答中解决这个问题。希望对您有所帮助。
【解决方案3】:

试试这个:

SELECT  * 
FROM    table1 
WHERE   Name2 REGEXP Name1;

或者对两个方向都这样做:

SELECT  * 
FROM    table1 
WHERE (
CASE WHEN (SELECT Name2  REGEXP Name1) = 0 THEN 
0 ELSE 
(SELECT Name1 REGEXP Name2)
END) = 0;

【讨论】:

  • SELECT 'Steve' REGEXP 'Steven' 返回0
  • 但是SELECT 'Steven' REGEXP 'Steve'的结果是1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多