SQL包含和排除

时间:2014-08-23 23:36:38

标签: mysql sql

属性列表包含列表id(lid)和所有者id(cid)。

hasA,hasB,hasC表是属性的描述符。 hasA持有有效代码,如:For Sale,For Rent等hasB持有状态代码如:New Price,Just Listed,Under Contract等hasC持有描述代码如:Single Family,Land,Farm,Waterfront,Mountain View,Log,etc 。

我没有预见到在hasA表中使用排除,但我确实需要在hasB和hasC表中。

在这个例子中,我将仅引用这些数字。一切都可以复制到sqlfiddle。

create table listings (
  lid integer, 
  cid integer 
);

create table hasA (
  lid integer,
  cid integer,
  id integer
 );

create table hasB (
  lid integer,
  cid integer,
  id integer
 );

create table hasC (
  lid integer,
  cid integer,
  id integer
 );


insert into listings values
(901,4),(902,4),(903,4),(904,4),(905,4),(906,4),(907,4),(908,5);

insert into hasA values
(901,4,333),(902,4,333),(903,4,333),(904,4,333),(905,4,333),(906,4,333),(907,4,444),(908,5,333);

insert into hasB values
(901,4,700),(901,4,707),(902,4,702),(902,4,701),(903,4,701),(904,4,708),(905,4,708);

insert into hasC values
(901,4,2000),(901,4,2001),(902,4,2000),(902,4,2003),(903,4,2000),(903,4,2001),(904,4,2015);

我需要帮助构建两个查询。可能这已经得到了回答,但我确实查看了现有的解决方案,无法对我的问题应用其他解决方案。我的SQL是基本的。

在第一个查询中,我包括hasA中包含333的所有列表,以及hasC中的2001。使用IN()对我的结果一直很好。结果集:901,903。

select a.lid 
  from listings a, hasA b, hasC c 
 where a.lid = b.lid 
   and a.lid = c.lid 
   and a.cid = b.cid 
   and a.cid = c.cid 
   and b.id in (333) 
   and c.id in (2001);

现在我正在尝试获取hasC无法包含2001的所有列表。基于视觉推理,所需的结果集为:902,904,905,906。我使用下面的NOT IN()不起作用。

select a.lid 
  from listings a, hasA b, hasC c 
 where a.lid = b.lid 
   and a.lid = c.lid 
   and a.cid = b.cid 
   and a.cid = c.cid 
   and b.id in (333) 
   and c.id not in (2001);

下一个示例涉及相同的逻辑,但包括hasB和hasC表。

直接前向包含工作正常并给出结果集:903

select a.lid 
  from listings a, hasA b, hasB c, hasC d 
 where a.lid = b.lid 
   and a.lid = c.lid 
   and a.lid = d.lid 
   and a.cid = b.cid 
   and a.cid = c.cid 
   and a.cid = d.cid 
   and b.id in (333) 
   and c.id in (701) 
   and d.id in (2001)

现在我试图获取hasC不能包含2001的所有列表。基于视觉推理,所需的结果集是:902。我对下面的NOT IN()的使用不起作用。

select a.lid 
  from listings a, hasA b, hasB c, hasC d 
 where a.lid = b.lid 
   and a.lid = c.lid 
   and a.lid = d.lid 
   and a.cid = b.cid 
   and a.cid = c.cid 
   and a.cid = d.cid 
   and b.id in (333) 
   and c.id in (701) 
   and d.id not in (2001)

如果基于视觉扣除的我想要的结果集不正确,请更正此。

1 个答案:

答案 0 :(得分:1)

我相信你正在处理“set-within-sets”查询。我喜欢group byhaving来解决这个问题,正是因为它非常灵活。

以下内容适用于您的上一次查询:

select a.lid
from listings a left join
     hasA b
     on a.lid = b.lid left join
     hasB c
     on a.cid = c.cid left join
     hasC d
     on a.lid = d.lid
group by a.lid
having sum(b.id in (333)) > 0 and
       sum(c.id in (701)) > 0 and
       coalesce(sum(d.id in (2001)), 0) = 0;

having子句中的每个条件都对应于其中一个条件。 > 0表示条件在列表中找到。 = 0表示不是。{/ p>

SQL小提琴是here

在您的情况下,由于值来自不同的表格,您还可以使用existsnot exists

select a.*
from listings a
where exists (select 1 from hasA b where a.lid = b.lid and b.id in (333)) and
      exists (select 1 from hasB c where a.cid = c.cid and c.id in (701)) and
      not exists (select 1 from hasC d where a.lid = d.lid and d.id in (2001));

使用适当的索引,这在MySQL中可能会更快。