【问题标题】:How to get rows by filtering multiple columns which contain values in MySQL?如何通过过滤包含 MySQL 中值的多个列来获取行?
【发布时间】:2018-11-06 12:11:47
【问题描述】:

我有一个名为 fruits 的 MySQL 表:

我想从表中选择行,其中列 fruit1fruit2fruit3 仅包含集合 [apple, pear, melon] 中的值,即

  1. 只有apple
  2. 只有pear
  3. 只有melon
  4. 只有applepear
  5. 只有applemelon
  6. 只有pearmelon
  7. 只有applepearmelon

对于我的示例数据,行将是 1, 5, 8

我试过这个查询:

SELECT * FROM fruits WHERE 'apple' in (fruit1, fruit2, fruit3)

这给了我在fruit 列之一中包含apple 的所有行。但是我不确定如何过滤多个列。

【问题讨论】:

  • 在最基本的层面上,您需要在 WHERE 中为 7 个条件中的每一个提供一个子句(或一组子句)。这些子句可能会被 OR 加入
  • 感谢您的评论,但如果元素超过 10、20 或更多怎么办?
  • 这看起来像是糟糕的设计。理想情况下,您应该为区域内的每个水果排一排。
  • @Hoo 那么你有很多工作要做。但正如 P. Salmon 所说,您的数据看起来是非规范化的,这可能导致难以编写这样的查询。
  • 如果有第四个水果呢?

标签: mysql


【解决方案1】:

理想解决方案:

您可以改为创建两个表来对其进行规范化。第一个表将存储idarea

Create Table area_master (area_id int auto_increment, 
                          area varchar(32), 
                          description varchar(32)
                          Primary Key(area_id));
Insert into area 
       (area, description) 
Values ('areaA', 'Hi'), 
       ('areaB', 'Hello'); -- add more rows as you need

第二个表将存储area_id(来自area_master 表的FK)、attributevalueattribute_order

Create Table area_fruits (id int auto_increment, 
                          area_id int, 
                          attribute varchar(32), 
                          value varchar(32), 
                          attribute_order int
                          Primary Key(id));

Insert into area_fruits 
       (area_id, attribute, value, attribute_order) 
Values (1, 'fruit', 'apple', 1),
       (2, 'fruit', 'banana', 1),
       (2, 'fruit', 'apple', 3); -- add more rows as needed

现在,获取数据的查询将如下所示,您无需每次都更改它,因为您添加了新水果 :-)

SELECT am.id, am.area, am.description 
FROM area_master AS am 
JOIN area_fruits AS af ON af.area_id = am.area_id AND 
                          af.attribute = 'fruit' 
GROUP BY am.id, am.area, am.description 
HAVING SUM(af.value IN ('apple', 'pear', 'melon')) = COUNT(*) 

P.S:可以进行更多改进,例如添加Foreign Key constraints等。但我希望你能明白要点。


原问题:

  • 使用Where,我们只考虑至少一个水果列具有applepearmelon 的行
  • 现在,我们在id 上执行Group By,并使用Having 忽略水果列不为空的行,它们的值不是
    applepearmelon

尝试以下方法:

SELECT id 
FROM fruits 
WHERE fruit1 IN ('apple', 'pear', 'melon') OR 
      fruit2 IN ('apple', 'pear', 'melon') OR 
      fruit3 IN ('apple', 'pear', 'melon')
GROUP BY id 
HAVING SUM(fruit1 NOT IN ('apple', 'pear', 'melon') AND fruit1 <> '') = 0 AND 
       SUM(fruit2 NOT IN ('apple', 'pear', 'melon') AND fruit2 <> '') = 0 AND 
       SUM(fruit3 NOT IN ('apple', 'pear', 'melon') AND fruit3 <> '') = 0

P.S 这是一个糟糕的设计。只要您有 n 个相似的列,就应该考虑对其进行规范化。

【讨论】:

  • 感谢您的回答!你对设计我的数据库有什么建议吗?实际上,我是 DB 的新手!
  • @Hoo 您将表结构更改为:idfruit_novalue。您可以阅读实体属性值 (EAV) 模式:stackoverflow.com/questions/494158/…
  • 感谢您的真诚回答!我想我以后可以解决你所有的烦恼。但是在这一刻,我慢慢意识到我需要分隔表:) 如上所述,实际上,我是编程的初学者,我正在自己制作 App。这是我第一次将电子表格转换为数据库。我的数据库方法仍然在电子表格上。我没有“试错”的经验来判断这种情况,因为这是我对数据库的第一次尝试。或许,以后我能完全理解你所有的担心。但是,感谢您的回答,我可以走得更远!再次感谢您!
  • @Hoo 在我个人看来,如果你有机会在早期修复结构,你应该这样做。代码一旦变大,就有自己的惯性,难以重构
  • 你是对的!我需要提前学习数据结构理论(规范化等)。谢谢!
【解决方案2】:

我假设您表中的空白 fruit 值是 NULL。在这种情况下,此查询将为您提供所需的结果:

SELECT *
FROM fruits
WHERE (fruit1 IN ('apple', 'melon', 'pear') OR fruit1 IS NULL)
  AND (fruit2 IN ('apple', 'melon', 'pear') OR fruit2 IS NULL)
  AND (fruit3 IN ('apple', 'melon', 'pear') OR fruit3 IS NULL)

输出

id  fruit1  fruit2  fruit3
1   apple       
5   melon           pear
8   apple           melon

如果空白值只是空字符串,则将查询更改为

SELECT *
FROM fruits
WHERE (fruit1 IN ('apple', 'melon', 'pear') OR fruit1 = '')
  AND (fruit2 IN ('apple', 'melon', 'pear') OR fruit2 = '')
  AND (fruit3 IN ('apple', 'melon', 'pear') OR fruit3 = '')

Demo on dbfiddle

【讨论】:

  • 非常感谢,这正是我所需要的!
  • @hoo 是吗? - 每次添加另一列时,您都必须更改查询。
  • @P.Salmon 实际上,我是 DB 的初学者。这是我第一次做DB。坦率地说,每次添加另一列时,我都没有遇到过更改查询的麻烦。我只是想我可以在这段代码上添加 1 行。当然,我认为它不会给这项服务的用户带来任何问题。你有什么经验可以让我学习吗?
  • @Nick 你会回答我的问题吗?我被禁止提问。请帮我。谢谢。
  • @Hoo 我已经编辑了您的问题以使其更好地阅读。您需要对其他一些被否决的问题执行相同的操作,因为您确实需要对问题投反对票的用户删除他们的投反对票。
猜你喜欢
  • 2020-05-02
  • 1970-01-01
  • 1970-01-01
  • 2020-06-22
  • 1970-01-01
  • 1970-01-01
  • 2019-06-24
  • 1970-01-01
  • 2022-12-02
相关资源
最近更新 更多