查询多个EXIST

发布于 2024-10-12 22:08:32 字数 476 浏览 6 评论 0原文

我有一个房间和设备的数据库。我想查询数据库并返回带有电视、收音机、卫星和冰箱等房间的列表(eq1、eq2、eq3、...、eqN)。

我有以下 SELECT 语句:

select * from rooms r where 
exists (select id from equipments where eq_id='eq1' and room_id=r.id)
and
exists (select id from equipments where eq_id='eq2' and room_id=r.id)
and
exists (select id from equipments where eq_id='eq3' and room_id=r.id)
.......
and
exists (select id from equipments where eq_id='eqN' and room_id=r.id)

有什么方法可以优化或缩短它吗?

I've got a database of rooms and equipments. I want to query the database and return a list of rooms with e.g. tv, radio, sat and fridge (eq1, eq2, eq3, ...., eqN).

I have the following SELECT statement:

select * from rooms r where 
exists (select id from equipments where eq_id='eq1' and room_id=r.id)
and
exists (select id from equipments where eq_id='eq2' and room_id=r.id)
and
exists (select id from equipments where eq_id='eq3' and room_id=r.id)
.......
and
exists (select id from equipments where eq_id='eqN' and room_id=r.id)

Is there any way to optimize or making this shorter?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

梦忆晨望 2024-10-19 22:08:32

要缩短您可以

select * 
from rooms r 
where @N = (select count(distinct eq_id) 
            from equipments 
            where eq_id IN ('eq1','eq2',...,'eqN') and room_id=r.id)

编辑
但不确定它是否真的会使其更快...恰恰相反,带有 EXISTS AND EXISTS 的版本有机会在第一个 false 上修剪执行分支,上面必须实际计算不同的值(遍历所有记录)并且看看那个值是什么。

所以你应该考虑什么更快:

  • 遍历一次与房间相关的所有记录(一个相关子查询)或
  • 为每个房间运行N(最坏情况)相关(但高度选择性的子查询)

这取决于你的数据统计(我会认为如果大多数房间没有所有所需的设备,那么您的初始版本应该更快,如果大多数房间都有所有设备,那么如果 EXISTS 版本更快,那么建议的版本可能会表现更好;首先是最有可能失败的查询,即首先检查最稀有的设备)

您也可以尝试使用 GROUP BY 的版本

select r.* 
from rooms r join
     equipments e on r.id = e.room_id
group by r.id
where eg_id in ('eq1','eq2',...,'eqN')
having count(distinct e.eq_id) = @N

(以上 SQL 未测试)

To shorten you could

select * 
from rooms r 
where @N = (select count(distinct eq_id) 
            from equipments 
            where eq_id IN ('eq1','eq2',...,'eqN') and room_id=r.id)

EDIT
but not sure if it will actually make it faster... quite the opposite, the version with EXISTS AND EXISTS has a chance to prune execution branch on the first false, the above must actually count the distinct values (go through all records) and see what that value is.

So you should think what is faster:

  • going once through all records related to a room (one correlated subquery) or
  • running N (worst case) correlated (but highly selective subqueries) for each room

It depends on the statistics of your data (I would think that if most rooms don't have all the sought equipment in them then your initial version should be faster, if most rooms have all equipment in them then the proposed version might perform better; also if the EXISTS version is faster make an effort to first the queries that are most likely to fail i.e. first check for rarest equipment)

You can also try a version with GROUP BY

select r.* 
from rooms r join
     equipments e on r.id = e.room_id
group by r.id
where eg_id in ('eq1','eq2',...,'eqN')
having count(distinct e.eq_id) = @N

(above SQL not tested)

身边 2024-10-19 22:08:32

试试这个(我没有任何数据库可用于测试它,还要考虑性能)

select * from
    rooms r,
    (
        select count(distinct id) as cnt, id from equipments  where eq_id in ('eq1','eq2') group by id
    ) as sub
where sub.id = r.id 
and sub.cnt >= 2 'Options count

注意:2 - 这是您需要的选项数量。例如,它们是:“eq1”、“eq2”

try this (I don't have any DB available to test it, also consider performance )

select * from
    rooms r,
    (
        select count(distinct id) as cnt, id from equipments  where eq_id in ('eq1','eq2') group by id
    ) as sub
where sub.id = r.id 
and sub.cnt >= 2 'Options count

Note: 2 - it is the number of options that you need. In example they are: 'eq1','eq2'

倾其所爱 2024-10-19 22:08:32
select * from rooms r where 
(select count(id) from equipments where eq_id='eq1' and room_id=r.id) > 0
and
...
select * from rooms r where 
(select count(id) from equipments where eq_id='eq1' and room_id=r.id) > 0
and
...
橘虞初梦 2024-10-19 22:08:32

使用存储过程。

以下是 mysql 的过程:

DELIMITER $

CREATE DEFINER=`root`@`%` PROCEDURE `GetRooms`(IN roomtable TEXT, IN equipmenttable TEXT, IN equipments TEXT )
BEGIN
    DECLARE statement text;
    DECLARE Pos int;
    DECLARE cond text;
    DECLARE element text;   
    DECLARE tmpTxt text;   

    set tmpTxt = equipments;
    set cond = "";
    set Pos = instr(tmpTxt,';');
    while Pos <> 0 do
    set element = substring(tmpTxt, 1, Pos-1);

    if cond <> "" then
        set cond = concat(cond,' and ');
    end if;

    set cond = concat(cond,'exists (select id from ' , equipmenttable ,' where eq_id=''' ,  element ,''' and room_id=r.id) ');


    set tmpTxt = replace(tmpTxt, concat(element,';'), '');


    set Pos = instr(tmpTxt,';');
    end while; 

    if tmpTxt <> "" then
        if cond <> "" then
            set cond = concat(cond,' and ');
        end if;

        set cond = concat(cond,'exists (select id from ' , equipmenttable ,' where eq_id=''' ,  tmpTxt ,''' and room_id=r.id) ');
    end if;

    SET @statement = concat('Select * FROM ' ,  roomtable , " WHERE " , cond , ";");

    PREPARE stmt FROM @statement;
    EXECUTE stmt;
END

执行方法: CALL GetRooms('RoomTableName','EquipmentTableName','EquipmentIDs')

示例:

Call GetRooms('rooms','equipemnts','eq1;eq2;eq3');

希望这会有所帮助。

Use an Stored Procedure.

here is the procedure for mysql:

DELIMITER $

CREATE DEFINER=`root`@`%` PROCEDURE `GetRooms`(IN roomtable TEXT, IN equipmenttable TEXT, IN equipments TEXT )
BEGIN
    DECLARE statement text;
    DECLARE Pos int;
    DECLARE cond text;
    DECLARE element text;   
    DECLARE tmpTxt text;   

    set tmpTxt = equipments;
    set cond = "";
    set Pos = instr(tmpTxt,';');
    while Pos <> 0 do
    set element = substring(tmpTxt, 1, Pos-1);

    if cond <> "" then
        set cond = concat(cond,' and ');
    end if;

    set cond = concat(cond,'exists (select id from ' , equipmenttable ,' where eq_id=''' ,  element ,''' and room_id=r.id) ');


    set tmpTxt = replace(tmpTxt, concat(element,';'), '');


    set Pos = instr(tmpTxt,';');
    end while; 

    if tmpTxt <> "" then
        if cond <> "" then
            set cond = concat(cond,' and ');
        end if;

        set cond = concat(cond,'exists (select id from ' , equipmenttable ,' where eq_id=''' ,  tmpTxt ,''' and room_id=r.id) ');
    end if;

    SET @statement = concat('Select * FROM ' ,  roomtable , " WHERE " , cond , ";");

    PREPARE stmt FROM @statement;
    EXECUTE stmt;
END

Execute it with: CALL GetRooms('RoomTableName','EquipmentTableName','EquipmentIDs')

Example:

Call GetRooms('rooms','equipemnts','eq1;eq2;eq3');

Hope this helps.

许久 2024-10-19 22:08:32

要更快地执行查询,请使用 Exists

select * 
from rooms as r 
where exists (
select * 
from equipments 
where eq_id IN ('eq1','eq2',..,'eqN') and r.id= equipments.room_id);

To Execute Query Faster use Exists

select * 
from rooms as r 
where exists (
select * 
from equipments 
where eq_id IN ('eq1','eq2',..,'eqN') and r.id= equipments.room_id);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文