我存储用户愿意将产品运送到varchar字段的目的地,如下所示:
"userId" "destinations" "product"
"1" "US,SE,DE" "apples"
"2" "US,SE" "books"
"3" "US" "mushrooms"
"1" "SE,DE" "figs"
"2" "UK" "Golf Balls"
我希望此查询会返回US
所在的所有行。相反,它只返回一行。
select * from destinations where destinations IN('US');
我如何做到这一点?我使用了错误的列类型吗?或者是我的查询失败了。
当前结果
US
预期结果
US,SE,DE
US,SE
US
答案 0 :(得分:3)
尝试使用FIND_IN_SET
select * from destinations where FIND_IN_SET('US',destinations);
答案 1 :(得分:1)
不幸的是,您构建表格的方式,您必须在字符串的开头,中间或结尾检查“US”的模式匹配。
你能做到的一种方法是使用LIKE,如下所示:
SELECT *
FROM destinations
WHERE destinations LIKE ('%US%');
另一种方法是使用REGEXP:
SELECT *
FROM destinations
WHERE destinations REGEXP '.*US.*';
另一个是使用FIND_IN_SET,正如Sadkhasan所解释的那样。
<强> CAVEAT 强>
但这些都不会提供出色的性能或数据完整性。当您为搜索添加条件时,他们都会将其性能问题复杂化。
E.g。使用Sadkhasan提出的FIND_IN_SET,你必须做类似的事情:
SELECT * FROM destinations
WHERE FIND_IN_SET('US',destinations)
OR FIND_IN_SET('CA',destinations)
OR FIND_IN_SET('ET',destinations);
使用REGEXP稍微好一些,虽然REGEXP本身很慢:
SELECT *
FROM destinations
WHERE destinations REGEXP '.*US|CA|ET.*';
现在怎么办?
您最好的选择是切换到3NF设计,目的地适用于产品,可以分成2个可以加入的表格,例如:
CREATE TABLE products (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
userId INT NOT NULL REFERENCES users(id),
name VARCHAR(255) NOT NULL
) TYPE=InnoDB;
然后你会添加所谓的复合密钥表,每行包含一个productId和一个国家,每个国家都有一行。
CREATE TABLE product_destinations (
productId INT NOT NULL REFERENCES products(id),
country VARCHAR(2) NOT NULL,
PRIARY KEY (productId, country)
) TYPE=InnoDB;
此表中的数据如下所示:
productId | country
----------|--------
1 | US
1 | CA
1 | ET
2 | US
2 | GB
然后你可以构建一个这样的查询:
SELECT p.*
FROM products AS p
INNER JOIN product_destinations AS d
ON p.id = d.productId
WHERE d.country IN ('US', 'CA', 'ET')
GROUP BY p.id;
在SELECT子句中添加GROUP(或DISTINCT)非常重要,因为单个产品可能会发送到多个国家/地区,导致多行匹配 - 聚合会将每个产品ID的结果减少到单个结果。
额外的好处是您不必更新您的国家/地区列并执行字符串操作以确定该国家/地区是否已存在。您可以让数据库为您执行此操作,并INSERT - 防止锁定问题,这将进一步加剧您的问题。
答案 2 :(得分:0)
如果您的目的地只有国家/地区的两个字符,则可以使用此选项。
SELECT * FROM destinations WHERE destinations LIKE ('%US%')
添加其他国家/地区
SELECT * FROM destinations WHERE destinations LIKE ('%US%')
AND destinations LIKE ('%SE%')
^^^--> you use AND or OR as you want the result.