sql 具有特定基数的查询(多对多)

sql query with an specific cardinality (many to many)

我在 Mariadb 中有三个表

sensor
+---------------+-------------+------+-----+---------+-------+
| Field         | Type        | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+-------+
| name          | varchar(64) | NO   | PRI | NULL    |       |
| reload_time   | int(11)     | NO   |     | NULL    |       |
| discriminator | varchar(20) | NO   |     | NULL    |       |
+---------------+-------------+------+-----+---------+-------+

sensor_common_service
+---------------+-------------+------+-----+---------+-------+
| Field         | Type        | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+-------+
| service_name  | varchar(64) | NO   | PRI | NULL    |       |
| sensor_name   | varchar(64) | NO   | PRI | NULL    |       |
+---------------+-------------+------+-----+---------+-------+

common_service
+---------------+-------------+------+-----+---------+-------+
| Field         | Type        | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+-------+
| service_name  | varchar(64) | NO   | PRI | NULL    |       |
| version       | int(11)     | NO   |     | NULL    |       |
| reload_time   | int(11)     | NO   |     | NULL    |       |
+---------------+-------------+------+-----+---------+-------+

而且我想获取所有 common_services 具有一组传感器的所有 common_services,例如,所有具有传感器温度和湿度的 common_services。

所以,如果我有

common_service1 : sensors [temperature]
common_service2 : sensors [temperature,humidity]
common_service3 : sensors [temperature,humidity, luminosity]

查询应该 return 只有 common_service2.

我的第一次尝试是尝试调整 Join between mapping (junction) table with specific cardinality

上的查询

这是结果

SELECT * FROM custom_service
JOIN (
      SELECT scm.service_name FROM sensor_custom_service scm
      WHERE scm.sensor_name IN (
                      SELECT s.name FROM sensor s
                      WHERE s.name='luminosity' OR s.name='temperature'
                      )
      GROUP BY scm.service_name HAVING COUNT(DISTINCT scm.sensor_name)=2
      ) AS jt
ON custom_service.service_name=jt.service_name;

另一个,

SELECT scs.* FROM sensor_custom_service scs
where scs.sensor_name IN ( 'luminosity', 'temperature' )
GROUP BY scs.service_name;
HAVING COUNT(scs.sensor_name) = 2

但是通过这个查询我也得到了有其他传感器的 common_services 因为 having count 只计算满足 where 子句的 sensor_custom_service。

使用上面的示例,这会同时查询 return

common_service2 : sensors [temperature,humidity]
common_service3 : sensors [temperature,humidity]

我认为将 INTERSECT 运算符与这样的东西一起使用这个查询会很容易

SELECT scs.* FROM sensor_custom_service scs
where scs.sensor_name IN ( 'luminosity', 'temperature' )
INTERSECT
SELECT scs.* FROM sensor_custom_service scs
HAVING COUNT(scs.sensor_name) = 2

但是玛丽亚布 returns

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'INTERSECT

因为它不受支持(我认为,因为两个查询都单独工作)


解决方案使用 9000 的查询。

select * 
from custom_service cs 
where 
  exists (select 1 from sensor_custom_service scs where 
      scs.service_name = cs.service_name and
      scs.sensor_name = 'luminosity') 
  AND
  exists (select 1 from sensor_custom_service scs where 
      scs.service_name = cs.service_name and
      scs.sensor_name = 'temperature')
  AND
  NOT exists (select  1  from sensor_custom_service scs where
      scs.service_name = cs.service_name and
      scs.sensor_name NOT IN ('temperature', 'luminosity'));

请注意,当您需要 AND 语义时,IN 会为您提供 OR 语义。基本上,如果您需要所有 3 个传感器,则需要 3 个连接,每个连接代表一个单独的传感器。类似于

select * 
from common_service cs 
where 
  exists (select 1 from sensor_common_service scs where 
          scs.service_name = cs.service_name and
          scs.sensor_name = 'luminosity') 
  AND
  exists (select 1 from sensor_common_service scs where 
          scs.service_name = cs.service_name and
          scs.sensor_name = 'temperature')
  -- add more sensors along these lines

这也意味着不可能编写适用于任意数量传感器的可变参数查询。 (我很想被证明是错误的!)

MariaDB 和 MySQL 具有 GROUP_CONCAT() 函数来完成此任务:

SELECT service_name,
       GROUP_CONCAT(sensor_name ORDER BY sensor_name) AS sensors
FROM sensor_common_service
GROUP BY service_name
HAVING sensors='humidity,temperature'

http://sqlfiddle.com/#!9/2f574/1

您要做的就是计算 'Temperature' 和 'Humidity' 出现的次数以及每个服务出现的其他任何次数。结果应该是第一个数字 = 2(都显示)和第二个数字 = 0(没有其他显示)。

select  ss.Service_Name
from    Sensor_Service ss
group by ss.Service_Name
having  Sum( case when ss.Sensor_Name in( 'Temperature', 'Humidity' ) then 1 else 0 end ) = 2
    and Sum( case when ss.Sensor_Name in( 'Temperature', 'Humidity' ) then 0 else 1 end ) = 0;

这仅使用通用 SQL。我在 Oracle 和 MariaDB 上测试过它。它应该 运行 与大多数其他人一样。