MySql逗号分隔值并使用IN来选择数据

时间:2014-04-02 11:08:43

标签: mysql

我存储用户愿意将产品运送到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

3 个答案:

答案 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.