2012-03-29 47 views
4

我有一个SQL查询,我试图写,但我不太清楚如何让它工作。棘手的SQL查询,也许某种连接会工作?

我有三个表: “S”, “T” 和 “ST”(这是 “S” 和 “T” 之间的映射

table s 
======= 
primary key sID | val 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 
     a    0 
     b    1 
     c    5 
     d    6 
     e    7 



table t 
======= 
primary key tID | val 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 
     nul    -1 
     bbb    2 
     ccc    3 
     ddd    4 



table st 
======== 
foreign key sID | foreign key tID 
(unique)   | (multiple sID to one tID, meaning tID is not unique) 
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 
     a     ddd 
     b     ccc 
     c     nul 
     d     ccc 
     e     bbb 

因此,所有的'得。被映射到't',但不是真实't'的映射到默认/空值't'(nul)。

val的's'和't ”,这意味着如果表‘s’有一个1,则表‘T’不能有1


所以我的问题是以下几点:

给定一组val(可以是's'或't'),我需要获取相应ID的'st'表中的sID和tID 。问题是,如果's'在集合中,但't'不在集合中,我需要获取值(sID,'nul')而不是(sID,tID)。例如,给定值(3,1,6),它将返回对:(b,ccc); (d,CCC);

给出的值(0,4),这将返回该对:(A,DDD)

然而,给出的值(6),这将需要返回(d,NUL),因为val 3(它对应于d被映射到的ccc)不在集合中。虽然我不需要null',只是null't'。


我想用下面的语句,但这并不能帮助我与返回的“ID,NUL”如果只是“S”是在集合而不是“T”。

SELECT st.sID, st.tID FROM t, st WHERE t.tID=st.tID AND (t.val=%_VAL1_% OR t.val=%_VAL_2 OR ......); 

这给了我什么,既具有“T”,并在集合中的“S”(实际上它给了我所有的'任何“T”是在集合相关),但它不给我自己的's'。

也许我可以后处理's'中的任何东西,但不是'st',但我想尽量避免这种情况。

有没有人有任何建议?我很困难。

谢谢!

(注:S,T,和ST是不是我真正的表名,不用担心另外,主键的实际上是文本的GUID,很遗憾,但我试图使它更易于区分。)

+0

如果我没有得到它错了,从你的模型和数据,我可以看到你有3个表来模拟一个一对多的关系。为什么不使用2?你说'所有'必须映射到't',那么为什么不把'F'加到's'中? – 2012-03-29 16:02:24

+0

它主要是为了扩展性的目的,并保持接口有点类似于他们在我开始使用sqlite之前。 – Jordan 2012-03-29 16:05:30

+0

这个数据库模型看起来很奇怪。你能分享更多关于它的动机 - 也许不同的模式会更好地为你服务?另外,在'st'中存在的行'{c,null}'和这个行根本不存在有什么区别? – 2012-03-29 16:17:03

回答

3

这里是你想要的东西:

SELECT s.sID, t.tID 
FROM st 
    LEFT JOIN s 
     ON s.sID = st.sID AND s.val IN (6) 
    LEFT JOIN t 
     ON t.tID = st.tID AND t.val IN (6) 
WHERE NOT(s.sid IS NULL AND t.tid IS NULL) 

这里是SQLFiddle that proves it for all 3 scenarios。您必须将映射表作为主数据,因为结果集来自可以为空的表。

虽然这是非常奇怪的逻辑。通常如果一个映射在多对多关系中没有命中,那么你只返回0行,而不是部分......

如果你想取消可能发生的重复映射失败(null in一列输出),然后添加一个DISTINCT。就像这样:

SELECT DISTINCT s.sID, t.tID 
FROM st 
    LEFT JOIN s 
     ON s.sID = st.sID AND s.val IN (6) 
    LEFT JOIN t 
     ON t.tID = st.tID AND t.val IN (6) 
WHERE NOT(s.sid IS NULL AND t.tid IS NULL) 

Here is the fiddle that shows the duplication

And, here is the one that fixes it

,最后,基于最新的更新。如果你想排除任何NULL列,只需让它成为INNER JOIN,然后你就可以删除NULL检查,因为你现在不会得到两个NULL ...如果你想要多个sID|NULL结果。

SELECT DISTINCT s.sID, t.tID 
FROM st 
    JOIN s 
     ON s.sID = st.sID AND s.val IN (6) 
    LEFT JOIN t 
     ON t.tID = st.tID AND t.val IN (6) 

Here is the SQLFiddle

+0

开始st也看起来不太有效,它适用于只有6的情况(尽管它也返回一堆空行),但如果我使用(6,3)它似乎返回正确的val,然后一堆空行,然后几行空sID,但相同的tID(ccc)。是的,我意识到这是非常奇怪的逻辑:p – Jordan 2012-03-29 16:18:26

+1

由于sqlfiddle隐藏了大量的空结果,您可能希望在末尾添加一个“WHERE NOT(s.sid IS NULL AND t.tid IS NULL)'' – 2012-03-29 16:19:07

+0

@JoachimIsaksson您是对的。更新了代码,并且很快就会有一个新的SQL小提琴链接 – 2012-03-29 16:20:57

0

你正在寻找一个LEFT JOIN

SELECT s.sid AS sid, t.tid AS tid 
    FROM s 
    LEFT JOIN st 
    ON s.sid = st.sid 
    LEFT JOIN t 
    ON t.tid = st.tid 
WHERE s.val IN (3, 1, 6) 
    AND t.val IN (3, 1, 6) 
+0

这不会在仅使用6 – 2012-03-29 16:10:42

+0

是的情况下工作,这就是问题所在。如果我只是使用“6”,它会返回整个集合(d,ccc) – Jordan 2012-03-29 16:12:45

+0

不行,但还是不对,请尝试一下我的sqlfiddle设置。现在,通过将过滤器逻辑置于由于s或t可以为空,所以你必须在从 – 2012-03-29 16:19:01