我有以下表格:
成员
这存储了我们系统的成员列表。
---------------------
| member_id | name |
---------------------
| 1 | Bob |
---------------------
| 2 | Joe |
---------------------
| 3 | Tom |
---------------------
| 4 | Bill |
---------------------
| 5 | Will |
---------------------
类别
这存储了我们系统的类别。默认情况下,类别对成员不可见。成员(member)必须拥有有效许可才能访问类别(见下文)。
----------------------
| cat_id | name |
----------------------
| 1 | Cat1 |
----------------------
| 2 | Cat2 |
----------------------
| 3 | Cat3 |
----------------------
许可证
存储成员拥有的许可证。一个成员可以拥有多个许可证。许可证可以有生命周期并且会过期。许可证过期后,成员(member)将无法再查看该类别。
------------------------------------------------------
| id | catid | subid | valid_from | valid_to |
------------------------------------------------------
| 1 | 1 | 1 | 2014-01-01 | 2020-12-01 |
------------------------------------------------------
| 2 | 1 | 2 | 1999-01-01 | 2001-01-02 |
------------------------------------------------------
| 3 | 1 | 3 | 2014-01-01 | 2020-12-01 |
------------------------------------------------------
| 4 | 1 | 4 | 1999-01-01 | 2000-01-01 |
------------------------------------------------------
| 5 | 1 | 5 | 2014-01-01 | 2020-12-01 |
------------------------------------------------------
| 6 | 2 | 1 | 2014-01-01 | 2020-12-01 |
------------------------------------------------------
| 7 | 2 | 2 | 1999-01-01 | 2001-01-02 |
------------------------------------------------------
| 8 | 2 | 3 | 2014-01-01 | 2020-12-01 |
------------------------------------------------------
| 9 | 2 | 4 | 1999-01-01 | 2000-01-01 |
------------------------------------------------------
| 10 | 2 | 5 | 2014-01-01 | 2020-12-01 |
------------------------------------------------------
| 11 | 3 | 1 | 2014-01-01 | 2020-12-01 |
------------------------------------------------------
| 12 | 3 | 2 | 2014-01-01 | 2020-12-01 |
------------------------------------------------------
偏好
偏好表存储成员(member)是否希望接收与类别相关的电子邮件。成员(member)可以将偏好设置为“1”表示“希望接收”或“0”表示“不希望接收”。一个怪癖是,如果成员没有记录(或空值),我们会假设他们希望接收。
-----------------------------------
| id | catid | subid | pref |
-----------------------------------
| 1 | 1 | 1 | 0 |
-----------------------------------
| 2 | 2 | 1 | 1 |
-----------------------------------
| 3 | 3 | 1 | 1 |
-----------------------------------
| 4 | 1 | 2 | 0 |
-----------------------------------
| 5 | 1 | 3 | 1 |
-----------------------------------
| 6 | 2 | 3 | 0 |
-----------------------------------
收件人
当根据类别发送电子邮件时,收件人会被记录下来,因此我们不会多次向他们发送电子邮件。
-----------------------------
| id | emailid | subid |
-----------------------------
| 1 | 1 | 1 |
-----------------------------
| 2 | 1 | 2 |
-----------------------------
我正在尝试编写一个查询来获取所有成员,以及他们针对一系列类别 ID 的相关许可、他们的偏好,并确保他们在收件人表中没有记录。
在伪查询中:
SELECT [all members, their licence info, and preference setting]
FROM [members table]
WHERE [member doesnt exist in the recipients table for a given emailID]
问题是我需要检查多个类别 ID,但只返回一个结果,并且仅当首选项设置为 1(或 null,或不存在)时才返回。
因此对于示例数据,假设我们正在搜索类别 ID 1、2 和 3(成员(member)必须至少拥有其中一个类别的许可证)并检查 emailID 1,唯一的结果应该是 member_id 3 (Tom) 的首选项 ID 为 6(因为它设置为 1)和许可证 ID 为 3(因为它有效并且首选项 ID 6 与其对应并且它被设置为 1)。第二个结果应该是 member_id 5 (Will),因为他有 catids 1 和 2 的许可,他没有收到 ID 为 1 的电子邮件,也没有特定的偏好设置。
原因是:成员 1 和 2 在 emailID 1 的收件人表中,成员 2 的许可证也已过期,成员 4 的许可证已过期并且成员 5 的首选项设置为 0。
我写的不太正确的查询是:
SELECT
members.member_id,
members.name,
licence.catid as licencedToCat,
categories.cat_name as categoryName,
licence.valid_from as licenceStart,
licence.valid_to as licenceEnd,
preferences.pref
FROM (`members`)
JOIN `licence` ON `licence`.`subid`=`members`.`member_id`
JOIN `preferences` ON `preferences`.`subid`=`members`.`member_id`
JOIN `categories` ON `categories`.`cat_id`=`licence`.`catid`
WHERE `licence`.`catid` IN (1,2,3)
AND `start_date` <= '2014-12-16'
AND `end_date` >= '2014-12-16'
AND (pref='1' OR pref IS NULL)
AND `members`.`member_id` NOT IN (SELECT subid FROM `recipients` WHERE `recipients`.`emailid`='1')
GROUP BY `licence`.`subid`
问题是查询返回的结果表明用户的首选项设置为 1,而实际上他们甚至没有该类别的记录集。
所需的输出是任何成员以及他们对该类别拥有的许可证,但前提是他们对该类别的偏好是 1/null/不存在,并且前提是他们没有出现在收件人表中对于给定的电子邮件 ID。
所以,如果一个成员有 2 个许可证
我很感激这是一篇很长的文章,如果你还在这里,谢谢!关于如何调整我的查询以解决此问题的任何想法?
最佳答案
我认为您的部分问题在于您使用的是所有内部联接。正如您所说,用户可能没有偏好,因此您的查询中可能不会返回一行。话虽如此,您似乎想要内部加入大多数表,因为看起来您只想要拥有许可证的成员,但您想要查看所有许可证,而不管该用户是否有偏好。所以,我将首选项设置为外部连接表:
SELECT m.*, l.catid AS licenseCat, c.name AS categoryName,
l.valid_from AS licenseStart, l.valid_to AS licenseEnd, p.pref AS preference
FROM members m
JOIN licenses l ON l.subid = m.member_id
JOIN categories c ON c.cat_id = l.catid
LEFT JOIN preferences p ON p.catid = c.cat_id AND p.subid = l.subid;
一旦我这样做了,我就写了一个子查询来提取收件人表中所有成员的 member_id 和指定的电子邮件:
SELECT subid
FROM recipients
WHERE emailid = 1;
现在您可以将其插入到您的原始查询中,并添加您的其他要求:
SELECT m.*, l.catid AS licenseCat, c.name AS categoryName,
l.valid_from AS licenseStart, l.valid_to AS licenseEnd, IFNULL(p.pref, 0) AS preference
FROM members m
JOIN licenses l ON l.subid = m.member_id
JOIN categories c ON c.cat_id = l.catid
LEFT JOIN preferences p ON p.catid = c.cat_id AND p.subid = l.subid
WHERE c.cat_id IN (1, 2, 3) AND
l.valid_from <= '2014-12-06' AND l.valid_to >= '2014-12-06' AND
m.member_id NOT IN (SELECT subid FROM recipients WHERE emailid = 1)
AND (p.pref = 1 OR p.pref IS NULL);
您在问题中说过这应该返回 member_id 3(即 Tom),但这与您的结果不符,因为成员 5 没有偏好,所以我们应该假设他们想要电子邮件,对吧?我也不确定如何为您分组。如果一个成员有多个订阅,你想保留哪一个?
我构建了一个 SQL Fiddle并测试了我所拥有的,它真的很接近。我希望这至少可以将您推向正确的方向,我会根据需要编辑答案。
编辑
以下会给你想要的,但并不总是推荐。如果您真的不关心订阅日期(只要它符合 where 子句中的条件)并且您真的不关心订阅的类别用户,只需添加 GROUP BY m.member_id
即可为每个成员获取一行。
关于MySQL从具有多个记录的多个表中选择最高连接值的单个记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27510406/