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

MySql逗号分隔值并使用IN选择数据,mysql,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" SELECT p.*

我将用户愿意将产品运送到的目的地存储在varchar字段中,如下所示:

"userId" "destinations" "product"
"1"      "US,SE,DE"     "apples"
"2"      "US,SE"        "books"
"3"      "US"           "mushrooms"
"1"      "SE,DE"        "figs"
"2"      "UK"           "Golf Balls"
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;
我希望这个查询将返回所有存在
US
的行。相反,它只返回一行

select * from destinations where destinations IN('US');
我怎样才能做到这一点?我是否使用了错误的列类型?或者是我的查询失败了

当前结果

US
US,SE,DE
US,SE
US
预期结果

US
US,SE,DE
US,SE
US

不幸的是,按照您构建表的方式,您必须在字符串的开头、中间或结尾检查“US”的模式匹配

一种方法是使用LIKE,如下所示:

SELECT * 
FROM destinations 
WHERE destinations LIKE ('%US%');
另一种方法是使用REGEXP:

SELECT * 
FROM destinations 
WHERE destinations REGEXP '.*US.*';
还有一种是使用FIND_IN_SET,正如Sadkhasan所解释的

警告

但是,这些都不能提供出色的性能或数据完整性。当你向搜索添加条件时,它们都会加剧性能问题

例如,使用Sadkhasan提出的在集合中查找,您必须执行以下操作:

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设计,通过将目的地拆分为两个您可以加入的表来应用于产品,例如:

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
然后您可以这样构造查询:

"userId" "destinations" "product"
"1"      "US,SE,DE"     "apples"
"2"      "US,SE"        "books"
"3"      "US"           "mushrooms"
"1"      "SE,DE"        "figs"
"2"      "UK"           "Golf Balls"
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子句中的DISTINCT)很重要,因为单个产品可能会发送到多个国家/地区,从而导致多行匹配-聚合会将这些结果减少为每个产品id的单个结果


另外一个好处是,您不必更新“国家”列并执行字符串操作来确定该国家是否已经存在。您可以让数据库为您执行此操作,并插入-防止锁定问题进一步加剧您的问题。

不幸的是,按照您构建表的方式,您必须在字符串的开头、中间或结尾检查“US”的模式匹配

一种方法是使用LIKE,如下所示:

SELECT * 
FROM destinations 
WHERE destinations LIKE ('%US%');
另一种方法是使用REGEXP:

SELECT * 
FROM destinations 
WHERE destinations REGEXP '.*US.*';
还有一种是使用FIND_IN_SET,正如Sadkhasan所解释的

警告

但是,这些都不能提供出色的性能或数据完整性。当你向搜索添加条件时,它们都会加剧性能问题

例如,使用Sadkhasan提出的在集合中查找,您必须执行以下操作:

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设计,通过将目的地拆分为两个您可以加入的表来应用于产品,例如:

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
然后您可以这样构造查询:

"userId" "destinations" "product"
"1"      "US,SE,DE"     "apples"
"2"      "US,SE"        "books"
"3"      "US"           "mushrooms"
"1"      "SE,DE"        "figs"
"2"      "UK"           "Golf Balls"
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子句中的DISTINCT)很重要,因为单个产品可能会发送到多个国家/地区,从而导致多行匹配-聚合会将这些结果减少为每个产品id的单个结果

另外一个好处是,您不必更新“国家”列并执行字符串操作来确定该国家是否已经存在。您可以让数据库为您执行此操作,并插入-防止锁定问题进一步加剧您的问题。

尝试使用FIND\u IN\u SET

select * from destinations where FIND_IN_SET('US',destinations);
尝试在集合中查找

select * from destinations where FIND_IN_SET('US',destinations);

如果您的目的地只有两个国家的字符,则可以使用此选项

    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.

如果您的目的地只有两个国家的字符,则可以使用此选项

    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.


如果它只包含“我们”怎么办?这段代码没有检索到它,是吗?我可以进行更改,因为这是开始。您的建议是什么?我强烈建议您阅读3NF设计,并以这种方式构建数据库,以优化性能和数据完整性。如果它只包含“我们”呢?这段代码没有检索到它,是吗?我可以进行更改,因为这是开始。您的建议是什么?我强烈建议您阅读3NF设计,并以这种方式构建您的数据库,以优化性能和数据完整性。首先,将数据标准化我建议您将目标放在
[…]
中的表中,如
[US][SE][DE]
然后,您可以通过执行
SELECT*从目的地获取包含
US
的记录,其中目的地(如('%[US]]')
@每行属于特定用户。用户识别码1将运送到美国,东南部,德国。。。等等,是的。因此需要标准化。您将有一个名为user_destination的表,它将每个用户与其各自的目的地配对。首先标准化您的数据。我建议您将目的地放入
[…]
字符内的表中,如
[US][SE][DE]
然后,您可以通过执行
SELECT*从目的地获取包含
US
的记录,其中目的地(如('%[US]]')
@每行属于特定用户。用户识别码1将运送到美国,东南部,德国。。。等等,是的。因此需要标准化。您将有一个名为user_destination的表,它将每个用户与其各自的目的地配对。+1是一个有趣的解决方案。但是,问题是,当您将国家/地区添加到搜索中时,您必须将查询与其他FIND_IN_SET匹配项组合起来。对于一个有趣的解决方案来说,这对性能是非常糟糕的。但是,问题是,当您将国家/地区添加到搜索中时,您必须使用