【问题标题】:Tricky SQL query, maybe some sort of join would work?棘手的 SQL 查询,也许某种连接会起作用?
【发布时间】:2012-04-13 07:28:49
【问题描述】:

我有一个我正在尝试编写的 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

所以,所有的 's' 都必须映射到一个 't',但那些不在真正的 't' 上的会被映射到默认/空的 't' (nul)。

val 在 's' 和 't' 中都是唯一的,这意味着如果 table 's' 有 1,则 table '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 's',只需要 null 't'。


我正在考虑使用以下语句,但如果只有 's' 在集合中而不是 't' 中,这并不能帮助我返回 "id, nul"。

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'。

也许我可以对“s”中但不在“st”中的任何内容进行后处理,但如果可能的话,我想避免这种情况。

有人有什么建议吗?我有点卡住了。

谢谢!

(注意:s、t 和 st 不是我的真实表名,别担心。此外,不幸的是,主键实际上是文本 GUID,但我试图使其更易于区分)

【问题讨论】:

  • 如果我没有弄错的话,从您的模型和数据中我可以看到您有 3 个表来建模一对多关系。为什么不只使用 2 个?你说all 's' have to be mapped to a 't',那为什么不在's'中的't'加上FK呢?
  • 它主要是出于可扩展性的目的,并让界面与我开始使用 sqlite 之前的界面有些相似。
  • 这个数据库模型看起来很奇怪。您能否分享更多有关它的动机 - 也许不同的模型会更好地为您服务?另外,st 中存在的行 {c, null} 与根本不存在的行之间有什么区别?
  • 如果 3,1,6 的值会返回 b,ccc 和 d,ccc,为什么 6 的值会返回 d,null? 6对应d,映射到ccc。还是我误读了什么?
  • @BrankoDimitrijevic:该模型在某种程度上来自尝试与使用 c++ std::maps 而不是关系数据库的预先存在的设置集成,尽管我同意它可能不是最佳选择。同样,返回 c, null 的需要来自 c++ 中我正在尝试做的事情的需要(特别是,基于 val 将数据库的子集合并到另一个数据库中)。

标签: sql sqlite select


【解决方案1】:

这是你想要的:

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 行,而不是部分......

如果您想消除可能因多次映射未命中(一列输出为空)而出现的重复项,请添加 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

AND 最后,基于最新更新。如果您想排除任何 NULL 的列,只需将 s 设为 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

【讨论】:

  • 这似乎也不太有效。它适用于只有 6 的情况(尽管它也返回一堆空行),但如果我使用 (6,3) 它似乎返回正确的 val,然后是一堆空行,然后是几行空 sID 但相同的 tID (ccc)。是的,我意识到这是非常奇怪的逻辑:p
  • 您可能需要在末尾添加WHERE NOT (s.sid IS NULL AND t.tid IS NULL),因为 sqlfiddle 隐藏了很多空结果。
  • @JoachimIsaksson 你是对的。更新了代码,很快就会有一个新的 SQL fiddle 链接
  • @JoachimIsaksson:是的,然后当我使用 (6,3) 它返回几行: (d,ccc); (NULL,ccc) ; (NULL,ccc) (至少在我的代码中,也许是因为我有额外的数据)。也许我可以稍后过滤它,但不确定为什么我会得到多个返回值。
  • @JustinPihony:如果我添加“distinct”,它会给我两个值(NULL,ccc)和(d,ccc)。你也明白了:sqlfiddle.com/#!3/8b560/3 旁注:Sqlfiddle 很棒,我不知道它存在。谢谢!
【解决方案2】:

您正在寻找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)

【讨论】:

  • 是的,这就是问题所在。如果我只使用 "6' 它会返回整个集合 (d, ccc)
  • 不,仍然不对。试试我的 sqlfiddle 设置。现在,您通过将过滤器逻辑放在 where 中,将 t 的左连接变成了内连接。由于 s 或 t 可以为 null,因此您必须在 from 中有 st
猜你喜欢
  • 1970-01-01
  • 2016-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多