我需要一个复杂的选择查询的帮助。我有消息表
CREATE TABLE Message(
MessageId int IDENTITY(1,1) NOT NULL, /*primary key*/
FromProfileId int NOT NULL, /*foreign key to Profile table*/
ToProfileId int NOT NULL, /*foreign key to Profile table*/
Datetime datetime NOT NULL,
MessageText ntext NOT NULL,
IsNew bit NOT NULL )
我需要一个接收用户ProfileId的存储过程并获取表消息联系人列表,如下所示:
ContactProfileId
(用户已发送消息或已收到消息的ProfileId
),AllMessagesCount
(所有发送/接收的消息都计入/来自ContactProfileId
),NewMessagesCount
(从ContactProfileId
收到的新邮件数),LastMessageDateTime
(最后一条消息日期时间从/到ContactProfileId
),ContactProfileId
个人资料信息。 任何想法如何才能实现这一结果?
已更新:以下是某些数据的示例
MessageId FromProfileId ToProfileId messageDatetime IsNew
--------------------------------------------------------------------
1 2 3 2010-11-20 18:16:40.230 1
2 2 3 2010-12-20 18:16:40.230 1
3 3 2 2010-10-20 18:16:40.230 0
4 3 4 2010-12-25 18:16:40.230 1
参数ProfileId = 3时SP的结果必须是
ContactProfileId AllMessagesCount NewMessagesCount LastMessageDateTime ContactName
---------------------------------------------------------------------------------------------------------------------
2 3 2 2010-12-20 18:16:40.230 Contact Name joined from Profile Table
4 1 0 2010-12-25 18:16:40.230 Contact Name joined from Profile Table
答案 0 :(得分:3)
你不清楚你想要得到什么,以及如何 - 你想从存储过程中取回你的值作为输出参数,还是作为输出结果集?
此外,您的要求列表中的第一点和最后一点非常模糊....你能详细说明吗?看起来好像你想要个人资料ID列表 - 对吧?
这是第一次尝试:
CREATE PROCEDURE dbo.DoSomething @ProfileID INT
AS BEGIN
DECLARE @AllMsgCount INT, @NewMsgCount INT
DECLARE @LastMsgDate DATETIME
-- count all messages
SELECT @AllMsgCount = COUNT(*)
FROM dbo.[Message]
WHERE (FromProfileId = @ProfileID OR ToProfileId = @ProfileID)
-- count new messages
SELECT @NewMsgCount = COUNT(*)
FROM dbo.[Message]
WHERE FromProfileId = @ProfileID
AND IsNew = 1
-- determine last datetime
SELECT @LastMsgDate = MAX([Datetime])
FROM dbo.[Message]
WHERE (FromProfileId = @ProfileID OR ToProfileId = @ProfileID)
-- return three values as a result set
SELECT @AllMsgCount, @NewMsgCount, @LastDateTime
-- ProfileId that user has sent message to or has received message from
-- select all profile ID's where the user has sent a message to, or
-- received a message from; return as a second result set
SELECT DISTINCT ToProfileId
FROM dbo.[Message]
WHERE FromProfileId = @ProfileID
UNION ALL
SELECT DISTINCT FromProfileId
FROM dbo.[Message]
WHERE ToProfileId = @ProfileID
-- return from profile data from the profile table
SELECT (list of columns)
FROM dbo.Profile
WHERE ProfileId = @ProfileID
END
另外:您可能希望为DATETIME
列找到比Datetime
更聪明的名称.....同样适用于Message
,这也是SQL Server保留字和不应该用作列名...
此外:基于单一责任原则,我认为使用单个存储过程从不同位置返回各种不同数据是一种糟糕的架构。我建议让存储过程返回一组数据 - 如果你需要三组数据,有三个程序.....使维护变得更容易!
更新:我的第二次尝试,利用了Richard提供的一些输入:我正在使用CTE(公用表表达式)来获取您可能感兴趣的数据。仍然:我相信您提供的样本数据有错误 - 请检查!
CREATE PROCEDURE dbo.DoSomething @ProfileID INT
AS
-- this CTE finds all distinct ProfileID's that the one passed in
-- as a parameter has had message exchange with
;WITH Contacts AS
(
SELECT DISTINCT ToProfileID AS 'ProfileID'
FROM dbo.Messages
WHERE FromProfileId = @ProfileID
UNION
SELECT DISTINCT FromProfileID AS 'ProfileID'
FROM dbo.Messages
WHERE ToProfileId = @ProfileID
)
SELECT
c.ProfileID,
COUNT(*) AS AllMessagesCount,
COUNT(CASE WHEN IsNEW = 1 AND FromProfileID IS NOT NULL THEN 1 ELSE NULL END) AS NewMessagesCount,
MAX(Datetime) AS LastMessageDateTime,
p.ContactName
FROM
Contacts c -- our CTE with the communication partners
INNER JOIN
Messages m ON (m.FromProfileId = c.ProfileID OR m.ToProfileId = c.ProfileID)
INNER JOIN
dbo.profiles p ON c.ProfileID = p.ProfileID
GROUP BY
c.ProfileID, p.ContactName
这会产生类似这样的输出(我冒昧地将一些样本ContactName
添加到Profiles
表中):
ProfileID AllMessagesCount NewMessagesCount LastMessageDateTime ContactName
2 3 2 2010-11-20 18:16:40.230 David
4 1 1 2010-12-25 18:16:40.230 Thomas
答案 1 :(得分:2)
SELECT m.*, p.Name AS ContactName
FROM (
SELECT
ContactProfileId,
AllMessagesCount = COUNT(*),
NewMessagesCount = COUNT(CASE IsNew WHEN 1 THEN 1 END),
LastMessageDateTime = MAX(messageDateTime)
FROM (
SELECT
ContactProfileId =
CASE ToProfileId
WHEN @ProfileId THEN FromProfileId
ELSE ToProfileId
END)
messageDateTime,
IsNew
FROM Message
WHERE @ProfileId IN (FromProfileId, ToProfileId)
) m
GROUP BY ContactProfileId
) m
INNER JOIN Profile p ON m.ContactProfileId = p.Id
或者,使用CTE:
WITH filteredmessages AS (
SELECT
ContactProfileId =
CASE ToProfileId
WHEN @ProfileId THEN FromProfileId
ELSE ToProfileId
END)
messageDateTime,
IsNew
FROM Message
WHERE @ProfileId IN (FromProfileId, ToProfileId)
),
groupedmessages AS (
SELECT
ContactProfileId,
AllMessagesCount = COUNT(*),
NewMessagesCount = COUNT(CASE IsNew WHEN 1 THEN 1 END),
LastMessageDateTime = MAX(messageDateTime)
FROM filteredmessages
GROUP BY ContactProfileId
)
SELECT m.*, p.Name AS ContactName
FROM groupedmessages m
INNER JOIN Profile p ON m.ContactProfileId = p.Id
答案 2 :(得分:0)
DECLARE @ProfileID int
SET @ProfileID = 1 --here you will need to set it to the profile id you want to query for
SELECT
count(*) AS AllMessagesCount,
count(CASE WHEN IsNEW = 1 AND FromProfileID IS NOT NULL THEN 1 ELSE NULL END) AS NewMessagesCount,
MAX(DateTime) AS LastMessageDateTime,
/*some other profile data from the profile table joined in*/
FROM
Message m JOIN
ProfileTable pt ON m.FromProfileID = pt.ProfileID
WHERE m.FromProfileID = @ProfileID OR m.ToProfileID = @ProfileID