【问题标题】:Select rows missing from link table选择链接表中缺少的行
【发布时间】:2012-10-25 21:23:58
【问题描述】:

我有 3 张桌子。一个是服务表。每个服务都可以有一些选项,这些选项在选项表中。第三个表是链接到每个选项的联系人列表。

所以,表格看起来像这样:

服务

serviceID
   100
   101

选项

optionID    serviceID    price
    1         100        10.5
    2         100        100
    3         101        25.25
    4         101        67.90

contactOptions

contactID     optionID     serviceID     priceOverride
    10            1           100              NULL
    10            2           100              NULL
    10            3           101              30
    10            4           101              100
    11            1           100              12
    11            3           101              NULL
    12            2           100              55
    12            3           101              NULL
    12            4           101              NULL
    13            1           100              NULL
    13            2           100              66
    13            4           101              NULL

如您所见,contactID 10 的所有 4 个选项都与他相关联。 11只有2个链接到她,12和13都只有3个。

我正在尝试找到一种方法来获取没有全部 4 个选项的联系人 ID,以及缺少哪些选项。

我试过了(SQL Fiddle:http://sqlfiddle.com/#!2/ca937/7):

SELECT *
FROM contactOptions
LEFT JOIN options
  ON options.optionID = contactOptions.optionID
  AND options.serviceID = contactOptions.serviceID

但这只是返回所有 12 行,我想要相反的,不存在的行

如何退回没有全部 4 个选项的联系人,以及他们没有的选项?

返回的行应该是:

contactID    optionID    serviceID
  11           2            100
  11           4            101
  12           1            100
  13           3            101

我最终想找出哪些服务(以及哪些选项)没有与之关联的联系人。

编辑:我已经越来越近了,这就是我现在所拥有的

SELECT contactID,
  contactOptions.serviceID,contactOptions.optionID AS x,
  options.optionID AS y
FROM contactOptions
JOIN options
  ON options.serviceID = contactOptions.serviceID
HAVING x != y

这是我当前的 SQL Fiddle:http://sqlfiddle.com/#!2/ca937/35

这会返回:

CONTACTID    SERVICEID    X    Y
  10           100        1    2
  10           100        2    1
  10           101        3    4
  10           101        4    3
  11           100        1    2
  11           101        3    4
  12           100        2    1
  12           101        3    4
  12           101        4    3
  13           100        1    2
  13           100        2    1
  13           101        4    3

这包含我想要的行,但它也包含我不想要的行。从这个结果中,我基本上需要删除联系人具有相同 X 和 Y 值的所有行,然后剩下的行是正确的。

编辑 2:我现在更接近了 (http://sqlfiddle.com/#!2/ca937/53):

SELECT contactID,
  contactOptions.serviceID,contactOptions.optionID AS x,
  options.optionID AS y
FROM contactOptions
JOIN options
  ON options.serviceID = contactOptions.serviceID
GROUP BY contactID,serviceID
HAVING x != y

这给了我:

CONTACTID    SERVICEID    X    Y
  12            100       2    1
  13            101       4    3

现在我有 2 行我想要。我只需要弄清楚如何获取 11 的行。

【问题讨论】:

    标签: php mysql join


    【解决方案1】:
    SELECT contacts_options_full.contactID, contacts_options_full.optionID, options.serviceID
    FROM
    (
      SELECT contactID, optionID
      FROM (SELECT optionID FROM options) o
      CROSS JOIN
      (SELECT DISTINCT contactID FROM contactOptions) co
    ) contacts_options_full
    LEFT JOIN contactOptions ON contacts_options_full.contactID = contactOptions.contactID
    AND contactOptions.optionID = contacts_options_full.optionID
    JOIN options ON contacts_options_full.optionID = options.optionID
    WHERE contactOptions.serviceID IS NULL
    ORDER BY contacts_options_full.contactID, contacts_options_full.optionID
    

    sqlfiddle

    【讨论】:

    • 哇!这样可行!谢谢 :-) 它有很多 JOIN 和子查询,但它可以工作 :-)
    【解决方案2】:
    SELECT *,COUNT(contactID)as cc
    FROM contactOptions
    GROUP BY contactID
    HAVING cc < 4
    

    当然要计算所有选项:

    SELECT COUNT(*) FROM options
    

    为 cmets 编辑:

    在您获得了之前查询的所有联系人 ID 之后,您可以为每个联系人 ID 运行此查询:

    SELECT optionID FROM options WHERE optionID NOT IN (SELECT optionID FROM contactOptions WHERE contactID = 11)
    

    【讨论】:

    • 事先不知道服务总数。
    • 我相信你可以用以前的查询来计算它。否则你无法解决这个问题
    • 这可以显示缺少选项的联系人,但我可以弄清楚这些选项是什么(无需使用 PHP)吗?
    • 我已经添加了最后一个查询...但我怀疑它可以在单个查询中完成。如果这对您有帮助,请注意 +1
    • 您的最后一个查询确实有帮助。很多。看看这个:sqlfiddle.com/#!2/ca937/81 BAM!正是我想要的。
    【解决方案3】:

    我在这方面做了更多工作,我想出了自己的答案。

    SELECT serviceContact.contactID, options.optionID,
    serviceContact.serviceID
    
    FROM options
    JOIN(
      SELECT DISTINCT contactID, services.serviceID, GROUP_CONCAT(optionID) as options
      FROM contactOptions
      JOIN services ON services.serviceID = contactOptions.serviceID
      GROUP BY contactID, services.serviceID
    ) serviceContact ON serviceContact.serviceID = options.serviceID
    
    WHERE NOT FIND_IN_SET(optionID,serviceContact.options)
    

    这里的窍门是将GROUP_CONCATNOT FIND_IN_SET 混合在一起。这让我可以在链接表中获得选项 not

    演示:http://sqlfiddle.com/#!2/ca937/87

    【讨论】:

    • @yes123:这在 SQL Fiddle 上似乎工作正常,但是当我在我的真实数据库(结构略有不同)上尝试时,它没有返回正确的行。在我的系统中,一个联系人可以有 0 个选项,该查询将联系人与每个服务连接起来,并返回给我可能的行。
    猜你喜欢
    • 1970-01-01
    • 2012-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-13
    • 2012-04-20
    • 2020-12-24
    相关资源
    最近更新 更多