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'
您要做的就是计算 '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 上测试过它。它应该 运行 与大多数其他人一样。
我在 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'
您要做的就是计算 '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 上测试过它。它应该 运行 与大多数其他人一样。