我有以下表格:
成员信息
这会存储我们系统的成员列表。
---------------------
| member_id | name |
---------------------
| 1 | Bob |
---------------------
| 2 | Joe |
---------------------
| 3 | Tom |
---------------------
| 4 | Bill |
---------------------
| 5 | Will |
---------------------
类别
这会存储我们系统的类别。默认情况下,成员看不到类别。会员必须拥有有效的许可才能访问某个类别(见下文)。
----------------------
| cat_id | name |
----------------------
| 1 | Cat1 |
----------------------
| 2 | Cat2 |
----------------------
| 3 | Cat3 |
----------------------
许可证
存储成员拥有的许可证。一个成员可以拥有许多许可证。许可证可以有使用寿命并且将过期。许可证到期后,该成员将无法再查看该类别。
------------------------------------------------------
| 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 |
------------------------------------------------------
偏好
首选项表存储成员是否希望接收与类别相关的电子邮件。会员可以设置' 1'希望收到'或者' 0' 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]
问题是我需要检查多个categoryID,但只返回一个结果,并且仅当首选项设置为1(或为null,或者不存在)时。
因此,对于示例数据,鉴于我们正在搜索categoryIDs 1,2和3(成员必须拥有至少一个这些类别的许可证)并检查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。 / p>
我写的不太正常的查询是:
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 /不存在且仅在他们不出现时在给定的emailID的收件人表中。
因此,如果会员有2个许可
我很欣赏这是一个很长的阅读,所以,谢谢,如果你还在这里!关于如何调整我的查询以解决这个问题的任何想法?
答案 0 :(得分:1)
我认为你的部分问题在于你正在使用所有内部联接。就像你说的那样,用户可能没有偏好,因此查询中可能不会返回一行。话虽这么说,似乎你想要内部加入大多数表,因为看起来你只想要拥有许可证的成员,但你想看到所有许可证,无论该用户是否有偏好。因此,我将首选项设置为外连接表:
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(这是汤姆)但是这与你的结果不符,因为成员5没有偏好,所以我们应该假设他们想要一封电子邮件吗?我也不确定如何为你分组。如果某个成员有多个订阅,您想要保留哪个订阅?
我构建了一个SQL Fiddle并测试了我拥有的内容并且非常接近。我希望这至少能帮助你朝着正确的方向前进,我会根据需要编辑答案。
修改强>
以下内容将为您提供所需内容,但并不总是建议您这样做。如果您确实不关心订阅日期(只要它符合where子句中的条件)并且您真的不关心类别用户,只需添加GROUP BY m.member_id
即可为每个成员获取一行。
答案 1 :(得分:0)
因此,最终的查询就像这些,测试和工作:
SELECT
m.member_id,
m.email,
l.catid as licencedToCat,
c.cat_name as categoryName,
l.valid_from as licenceStart,
l.valid_to as licenceEnd,
COALESCE(p.pref, 1) pref
FROM members m
JOIN licence l ON l.subid = m.member_id
JOIN categories c ON c.cat_id = l.catid
LEFT JOIN preferences p ON p.subid= m.member_id AND p.cat_id = l.cat_id
LEFT JOIN recipients r ON r.subid = m.member_id
WHERE l.catid IN (1,2,3)
AND start_date <= '2014-12-16' AND end_date >= '2014-12-16'
AND COALESCE(p.pref, 1) = 1
AND COALESCE(r.emailid, 0) = 0-- assuming with emailid = 0 it remains valid as recipient
GROUP BY m.member_id
但是,出于查询的目的,DISTINCT m.*
子句中只有SELECT
会丢弃GROUP BY