使用SQL

时间:2015-06-05 14:39:45

标签: mysql sql

我有一个PHP代码片段,它查找MySQL表并返回与给定搜索字符串相关的前6个最接近的匹配,包括精确匹配和部分匹配。 SQL语句是:

SELECT phone, name FROM contacts_table WHERE phone LIKE :ph LIMIT 6;

使用上面的示例,如果分配了:ph,例如%981%,它将返回包含981的每个条目,例如9819133333,+ 917981688888,9999819999等。但是,是否也可以使用相同的查询返回其值包含在搜索字符串中的所有条目?因此,如果搜索字符串为12345,则会返回以下所有内容:

  • 12345 6789(包含搜索字符串)
  • 8888 12345 00(包含搜索字符串)
  • 999 12345 (包含搜索字符串)
  • 123 (包含在搜索字符串中)
  • 45 (包含在搜索字符串中)
  • 2345 (包含在搜索字符串中)

3 个答案:

答案 0 :(得分:3)

您可以在列号为LIKE的位置进行查找:

SELECT * FROM `test` 
    WHERE '123456' LIKE CONCAT('%',`stuff`,'%') 
        OR `stuff` LIKE '%123456%';

但是,永远不会使用索引,因为索引不能与前面的%一起使用。

另一种方法是在内存中创建临时表并插入标记化字符串并在临时表上使用JOIN。这可能比我上面的解决方案慢得多,但它是一个潜在的选择。

答案 1 :(得分:1)

您可以尝试使用动态SQL选项:

SELECT
    phone
FROM
    contacts_table 
WHERE 
    phone LIKE :ph or
    phone = :val1 or
    phone = :val2 or
    phone = :val3 or
    phone = :val4 or
    phone = :val5 (so on a so forth)
LIMIT 6;

其中:ph将是您的常规输入(例如%981%),而valX将是标记化输入。

如果您巧妙地进行标记化(例如,如果输入长度为5,则输入标记大小为3或4)将是个好主意。尽量限制令牌的数量以获得更好的性能。

DEMO

如果您使用PHP,请执行以下操作:

foreach ($phone as getPhoneNumberTokens($input)) {
    if ($phone != "") {
        $where_args[] = "phone = '$phone'";
    }
} 
$where_clause = implode(' OR ', $where_args);

答案 2 :(得分:0)

您可以使用三个表格。不过,我实际上并不知道它的性能如何。我没有插入任何东西来测试它。

contact将包含所有联系人。 token将包含每个有效令牌。我的意思是当你插入contact时,你也会对电话号码进行标记,并将每一个标记插入token表。代币将是独一无二的。凯。那么,您将拥有一个relation表,其中包含many<->manycontact之间的token关系。

然后,您将获得所有具有与输入电话号码匹配的令牌的联系人。

表格定义:

CREATE TABLE contact (id int NOT NULL AUTO_INCREMENT, phone varchar(16), PRIMARY KEY (id), UNIQUE(phone));

CREATE TABLE token (id int NOT NULL AUTO_INCREMENT, token varchar(16), PRIMARY KEY (id), UNIQUE(token));

CREATE TABLE relation (token_id int NOT NULL, contact_id int NOT NULL); 

查询:
可能有更好的方法来编写这个查询(可能通过使用子查询而不是这么多的连接?),但这就是我提出的。

SELECT DISTINCT contact_list.phone FROM contact AS contact_input
  JOIN relation AS relation_input
    ON relation_input.contact_id = contact_input.id
  JOIN token AS all_tokens
    ON all_tokens.id = relation_input.token_id

  JOIN relation AS relation_query 
    ON relation_query.token_id = all_tokens.id
  JOIN contact AS contact_list
    ON contact_list.id = relation_query.contact_id

  WHERE contact_input.phone LIKE '123456789'

查询计划:
但是,这在数据库中实际上没有数据,因此如果存在数据,执行计划可能会发生变化。由于eq_ref和关键用法,我看起来很有希望。 enter image description here 我也让an SQL Fiddle证明了这一点。

注意:

  • 我没有添加任何索引。你可以添加一些索引和 让它更高效...但索引可能实际上没有帮助 这个例子,因为你没有查询任何重复的行。
  • 可以添加编译器提示或使用LEFT / RIGHT Joins来改进查询计划的执行。 LEFT / RIGHT加入错误的地方可能会破坏查询。
  • 目前的情况是,您必须将查询过的号码插入contact数据库并对其进行标记,然后在查询前插入relationtoken。相反,您可以使用临时表来查询令牌,然后执行JOIN temp_tokens ON temp_tokens.token = all_tokens.token ...实际上,这可能是您应该做的。但我现在不打算重写这个答案。
  • integerphone使用token列效果会更好,如果这是您的有效选项。

另一种方法,比将所有令牌插入表中只是为了查询更好的方法是使用IN (),如:

SELECT DISTINCT contact.phone FROM token
    JOIN relation 
        ON relation.token_id = token.id
    JOIN contact 
        ON relation.contact_id = contact.id
    WHERE token.token IN ('123','234','345','and so on')

这是另一个改进的小提琴:http://sqlfiddle.com/#!9/48d0e/2