MySql逗号分隔值并使用IN选择数据
我将用户愿意将产品运送到的目的地存储在varchar字段中,如下所示: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.*
"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匹配项组合起来。对于一个有趣的解决方案来说,这对性能是非常糟糕的。但是,问题是,当您将国家/地区添加到搜索中时,您必须使用