如何在MySQL中存储具有属性的唯一列表

问题描述

我想在MysqL中存储如下数据,它们分别在user_idlids上存储

recordid     user_id      lids       length     breadth
------------------------------------------------------------
1            1           l1,l2       10          5
2            1           l1          7           5
3            1           l1,l3,l2    10          10
4            1           l2,l3       25          15

我的查询模式是:

  • 请给我盖和长分别为l2,l1的长度和宽度
  • 请给我盖和长分别为l2,l3的长度和宽度

基本上,盖子的输入可以以任何顺序进行搜索,但仍应提供正确的长度和宽度。

因此,我们不应该将逗号分隔的值存储在RDBMS中。

问题-我应该如何构造数据库以使其具有不连续的user_id/lids组合,这些组合可以提供正确的长度和广度而无需太多的字符串操作?

我想出了一种解决方案,可以像这样查询数据库-

select * from table1 where find_in_set('l2',lids) AND find_in_set('l1',lids);

然后在代码中,确定计数准确为2个盖子。但这不是完美的解决方案。需要指导。

AddOn-不需要编写本机SQL查询的特定于SpringBoot + JPA(休眠)的解决方


根据注释,如果我为lids创建表-

recordid(fk)    lid
----------------------------------
1               l1
1               l2
2               l1
3               l1
3               l3
3               l2
4               l2
4               l3

那我将如何确保只有一种独特的盖子组合可供用户使用? 我的选择查询是什么?会是这样吗?

select * from table1,lids where main.recordid = lids.recordid and lid IN ('l2','l3');

IN运算符将运行OR而不是AND查询,这也会产生错误的结果。

我是否必须基于recordid表中的lids进行分组,然后应用where条件?抱歉,我读过许多与之相关的文章并分心了,我对此感到非常困惑。


好的,问题基本上可以归结为这个问题-How to find if a list/set is exactly within another list

我想找到具有 EXACT 个要搜索的盖子的记录ID。

解决方法

tl; dr:针对此类设计问题,请考虑实体 relationships ,amd sets

您有两个实体,recordslids。他们有一个多对多关系。

让我们调用第二个表records_lids,以表明它是记录和盖子之间的多对多关联表。它有两列record_idlid。如果该表中存在一行,则意味着所提到的record_id带有所提到的lid

该表的主键应由其两个列(record_id,lid)组成。由于主键是唯一的,因此可以防止任何记录多次具有相同的盖子。

现在,找到带有盖子l1的record_id值集很容易。您甚至不需要第一张桌子。

        SELECT record_id FROM records_lids WHERE lid = `l1` 

要查找具有多个盖子的记录,您需要将记录集与每个盖子的逻辑相交。您可以这样操作:(https://www.db-fiddle.com/f/cLf4b6LDwMH9eFRTTheZJr/0

SELECT record_id 
          FROM (SELECT record_id FROM records_lids WHERE lid = 'l1') l1
  NATURAL JOIN (SELECT record_id FROM records_lids WHERE lid = 'l2') l2
  NATURAL JOIN (SELECT record_id FROM records_lids WHERE lid = 'l3') l3 
 

NATURAL JOIN操作处理相交操作;结果仅包含具有匹配的record_id值的行。 (其他一些SQL表服务器品牌都具有INTERSECT运算符,但还没有MySQL ...)

您也可以这样(https://www.db-fiddle.com/f/cLf4b6LDwMH9eFRTTheZJr/1)。

SELECT record_id
  FROM records_lids
 WHERE lid IN ('l1','l2','l3')
 GROUP BY record_id
 HAVING COUNT(*) = 3

HAVING子句是您坚持要记录所有三个盖子的记录。

一旦有了record_ids集,就可以将其加入到另一个表中。 (https://www.db-fiddle.com/f/cLf4b6LDwMH9eFRTTheZJr/2

SELECT records.* 
          FROM (SELECT record_id FROM records_lids WHERE lid = 'l1') l1
  NATURAL JOIN (SELECT record_id FROM records_lids WHERE lid = 'l2') l2
  NATURAL JOIN (SELECT record_id FROM records_lids WHERE lid = 'l3') l3 
  NATURAL JOIN records

或(https://www.db-fiddle.com/f/cLf4b6LDwMH9eFRTTheZJr/3

SELECT * 
  FROM records
  WHERE record_id IN (
    SELECT record_id
      FROM records_lids
     WHERE lid IN ('l1','l3')
     GROUP BY record_id
     HAVING COUNT(*) = 3
)

编辑:我不完全理解您的问题。您想排除没有* exactly(匹配的盖子的记录。请尝试(https://www.db-fiddle.com/f/cLf4b6LDwMH9eFRTTheZJr/4)。这取决于MySQL的一个怪癖,即lid IN ('l1','l2')之类的布尔表达式为false时,值为0。如果为true,则为1。


SELECT * 
  FROM records
  WHERE record_id IN (
       SELECT record_id
         FROM records_lids
     GROUP BY record_id
       HAVING SUM(lid IN ('l1','l2')) = 2
          AND COUNT(*) = 2 
)

SQL本质上是一种用于操纵集合的语言。这里的设计技巧是

  1. 弄清楚您的实体
  2. 弄清楚它们之间的关系
  3. 弄清楚如何获取所需的实体集
  4. 检索需要与集合匹配的行