需要SQL查询的帮助

时间:2011-03-13 09:24:47

标签: sql sql-server-2008

我需要一个复杂的选择查询的帮助。我有消息表

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

3 个答案:

答案 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