我需要加入三个表:用户,通知和 UserNotification 。后者只是User和Notificaion之间的交叉引用表,其中列UserID(fk to User)和LastReadNotificationID(fk to Notification)。表UserNotification应包含对Web应用程序中用户通知功能中上次读取通知的引用。因此,如果我们在ID为1和2的Notification中有两条记录,而在UserNotification中有一条记录,其中fk为Notification = 1,则表示用户没有读取我想在下次登录时显示的最后创建的通知。 / p>
现在,我需要在登录时选择用户表中的所有列,并在结果集中添加另一列(通知)。 通知应为布尔值,如果符合以下情况,则应为false:
通知应为true:
问题是我无法编写满足上述所有要求的查询。我目前的查询有效,除非通知为空(因此是UserNotification)。在这种情况下,我的查询返回Notify = true;
如果已经尝试了许多不同的方法来解决(左连接,右连接,if,case when,ifnull等)这个但是我被卡住了。请帮忙。
我现在使用的查询:
SELECT ID, FirstName, LastName, Email, Password, Roles, LastLoginTime, LoginCount, Active,
(SELECT IFNULL((SELECT 0 FROM UserNotification UN, User U
WHERE UN.UserId = U.ID AND U.Email = :email
AND UN.LastReadNotificationID <=> (SELECT MAX(ID) FROM Notification WHERE Display = 1)), 1)) AS Notify
FROM User WHERE Email = :email;
3个表:
CREATE TABLE `User` (
`ID` bigint(20) NOT NULL AUTO_INCREMENT,
`FirstName` varchar(50) CHARACTER SET utf8 DEFAULT NULL,
`LastName` varchar(50) CHARACTER SET utf8 DEFAULT NULL,
`Email` varchar(50) CHARACTER SET utf8 DEFAULT NULL,
`Password` varchar(200) CHARACTER SET utf8 DEFAULT NULL,
`Roles` varchar(200) CHARACTER SET utf8 DEFAULT NULL,
`LastLoginTime` varchar(50) CHARACTER SET utf8 DEFAULT NULL,
`LoginCount` int(11) DEFAULT NULL,
`Active` bit(1) NOT NULL DEFAULT b'1',
PRIMARY KEY (`ID`),
UNIQUE KEY `Email` (`Email`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;
CREATE TABLE `UserNotification` (
`UserID` bigint(20) NOT NULL,
`LastReadNotificationID` int(11) NOT NULL,
UNIQUE KEY `UserID_UNIQUE` (`UserID`),
KEY `fk_UserNotification_Notification_ID` (`LastReadNotificationID`),
CONSTRAINT `fk_UserNotification_Notification_ID` FOREIGN KEY (`LastReadNotificationID`) REFERENCES `Notification` (`ID`) ON DELETE CASCADE,
CONSTRAINT `fk_UserNotification_User_ID` FOREIGN KEY (`UserID`) REFERENCES `User` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `Notification` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Text` text NOT NULL,
`Created` timestamp NOT NULL,
`UserID` bigint(20) NOT NULL,
`Display` bit(1) DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `fk_Notification_User_ID` (`UserID`),
CONSTRAINT `fk_Notification_User_ID` FOREIGN KEY (`UserID`) REFERENCES `User` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
答案 0 :(得分:0)
请扩展您想要的结果。您是否想要用户未读取的实际上次输入通知?或者你只是想知道你是否应该通知用户?
SELECT
u.Id
,u.FirstName
,u.LastName
,u.Email
,u.Password
,u.Roles
,u.LastLoginTime
,u.LoginCount
,u.Active
,CASE WHEN un.UserId IS NULL THEN 1 Notify
FROM
User u
CROSS JOIN Notification n
LEFT JOIN UserNotification un
ON u.ID = un.UserId
AND n.Id = un.LastReadNotificationId
WHERE
U.Email = :email
AND n.Display = 1
;
如果存在记录,此查询将为Users
到Notifications
以及join
UserNotifications
表提供每种可能的组合。它将指定通知用户尚未阅读的所有Notifications
。因此所有Notifications
都会重复用户。
然后,您可以限制通过使用案例陈述和分组来通知并指定用户未读取的最新通知。
SELECT
u.Id
,u.FirstName
,u.LastName
,u.Email
,u.Password
,u.Roles
,u.LastLoginTime
,u.LoginCount
,u.Active
,CASE
WHEN
SUM(CASE
WHEN un.UserId IS NULL THEN 1
ELSE 0
END) > 0 THEN 1
ELSE 0
END AS Notify
,MAX(CASE WHEN un.UserId IS NULL THEN n.ID END) AS NextNotification
FROM
User u
CROSS JOIN Notification n
LEFT JOIN UserNotification un
ON u.ID = un.UserId
AND n.Id = un.LastReadNotificationId
WHERE
U.Email = :email
AND n.Display = 1
GROUP BY
u.Id
,u.FirstName
,u.LastName
,u.Email
,u.Password
,u.Roles
,u.LastLoginTime
,u.LoginCount
,u.Active
如果用户已读取所有通知,则NextNotifcation
将为NULL,因为MAX忽略空值且case语句未指定,否则该值将为null。
如果您只是想知道是否应该通知用户,请忽略/删除NextNotifcation
列。
执行此操作的另一种方法与您的处理方式类似:
SELECT
u.Id
,u.FirstName
,u.LastName
,u.Email
,u.Password
,u.Roles
,u.LastLoginTime
,u.LoginCount
,u.Active
,CASE
WHEN EXISTS
(SELECT
*
FROM
Notifications n
LEFT JOIN UserNotifications un
ON n.Id = un.LastReadNotificationId
AND un.UserId = u.Id
WHERE
n.Display = 1
)
THEN 1
ELSE 0
END AS Notify
FROM
Users u
WHERE
u.Email = :email
;
通常情况下,我不喜欢在列定义中添加子选择,但是为用户查看BIGINT的数据类型,使用EXISTS而不是交叉连接实际上可能是更好的性能。
答案 1 :(得分:0)
您尝试的问题可能归结为一个基本问题:您希望将三个表与左连接(A -> B -> C
)链接在一起但您还希望能够推断是否还有其他行在表中不在连接逻辑和ID对之外。
B
和C
都可以为空,这就是为什么用左连接来接近它的原因很自然。 (B
&#34; s&#34;空虚&#34;当然是每个用户。)问题是B
位于中间,即使{{1}也可以为空}} 不是。但是当C
为空时,您无法根据加入的结果确定B
的任何结论。
C
您可能更喜欢在交叉连接上使用子查询:
select
u.ID as UserID, FirstName, LastName, Email, Password,
Roles, LastLoginTime, LoginCount, Active,
case
when max_n.IsEmpty = 1 or un.LastReadNotification = max_n.ID then 0
-- the following is equivalent and eliminates the IsEmpty flag entirely
-- when coalesce(max_n.ID, 0) = coalesce(un.LastReadNotification, 0) then 1
else 1 -- isn't it sufficient to just return 1 at this point?
-- when un.LastReadNotification is null then 1 -- notification wasn't empty btw
-- when un.LastReadNotification < agg_n.MaxID then 1 -- can't be greater, right?
end as Notify
from
User u
left outer join UserNotification un
on un.UserID = u.ID
cross join (
select
case when max(ID) is not null then 0 else 1 end as IsEmpty,
max(ID) as ID
from Notification
) max_n
最终,您真正需要知道的是,是否存在ID大于每个用户最后看到的ID的通知。只抓取最高通知ID就足以让您全面做出决定。
以下是另一个想法:在初始值为0的用户表中使用select
u.ID as UserID, FirstName, LastName, Email, Password,
Roles, LastLoginTime, LoginCount, Active,
case
when coalesce((select max(ID) from Notification), 0)
= coalesce(LastReadNotification, 0)
else 1
end as Notify
from User u left outer join UserNotification un on un.UserID = u.ID
以及在&#34中使用ID为0的虚拟行可能更容易吗? #34;通知表?基本上你根本不需要知道任何关于空虚的事情。如果你实现了0行,你最终会得到这样的结果:
LastReadNotification