半棘手的 SQL 查询

Posted

技术标签:

【中文标题】半棘手的 SQL 查询【英文标题】:Semi-Tricky SQL Query 【发布时间】:2008-10-22 16:00:21 【问题描述】:

我正在尝试为 SQL Server 2005 编写查询,但我不知道该怎么做。我有一个包含以下字段的表格:

MessageID int 类别ID int 优先级 tinyint 消息文本 NVARCHAR(MAX)

我需要一个查询,该查询将为类别中具有最高优先级的每一行返回 *。例如,如果我有以下数据:

MessageID、CategoryID、优先级、MessageText 1, 100, 1, 发生错误 #1234 2, 100, 2, 发生错误 #243 3, 100, 3, 发生错误 #976 4、200、4,发生错误 #194 5, 200, 1, 发生错误 #736 6、300、3,发生错误 #54 7, 300, 2, 发生错误 #888

那么结果将是:

MessageID、CategoryID、优先级、MessageText 3, 100, 3, 发生错误 #976 4、200、4,发生错误 #194 6、300、3,发生错误 #54

请注意,它为每个类别返回一行,并且该行是该类别的优先级最高的行。

谁能告诉我如何编写这个查询?

【问题讨论】:

您可以使用 Oracle 中的分析函数更轻松地编写它。 :) 或者使用SQL Server中的解析函数,不用移植到Oracle。 【参考方案1】:

已验证:

SELECT
    highest_priority_messages.*
FROM
(
    SELECT
    m.MessageID
    , m.CategoryID
    , m.Priority
    , m.MessageText
    , Rank() OVER 
        (PARTITION BY m.CategoryID ORDER BY m.Priority DESC) AS p_rank
    FROM [Message] m
    GROUP BY 
        m.CategoryID 
        , m.Priority
        , m.MessageID
        , m.MessageText
) highest_priority_messages
WHERE 
    p_rank = 1

【讨论】:

您还需要添加一个 CreatedDate 并将其计算到订购等式中,我敢肯定。 你确定这里没有不使用 Rank() 就可以使用 PARTITION 的方法吗? 您可以将 MAX(m.Priority) OVER (PARTITION BY m.CategoryID) 作为 MaxPriority,但是 WHERE 必须将优先级与 MaxPriority 进行比较。这还取决于您是否想要最高优先级的倍数。 这里GROUP BY有什么用?【参考方案2】:

我相信这应该可行,表名假定为 Messages

SELECT
    M.MessageId,
    M.CategoryId,
    M.Priority,
    M.MessageText
FROM 
(
    SELECT 
        CategoryId,
        MAX(Priority) AS Priority
    FROM Messages
    GROUP BY CategoryId
) AS MaxValues
    INNER JOIN Messages M
        ON (MaxValues.CategoryId = M.CategoryId
                AND MaxValues.Priority = M.Priority)

注意

此方法中唯一的“陷阱”是,如果您有多个最大优先级...

【讨论】:

【参考方案3】:

如果您想在没有所有子查询的情况下这样做:

SELECT
     MessageID,
     CategoryID,
     Priority,
     MessageText
FROM
     dbo.Messages M1
LEFT OUTER JOIN dbo.Messages M2 ON
     M2.CategoryID = M1.CategoryID AND
     M2.Priority > M1.Priority
WHERE
     M2.MessageID IS NULL

您可能需要根据处理关系的方式调整查询。你没有任何这样的例子,所以我不确定。

【讨论】:

【参考方案4】:
select distinct query1.* from

(select categoryId,msgText,max(priorityId) as MAX_PRIORITY
 from message
  group by categoryId,msgText
 order by categoryId
) query1,


(select categoryId,max(priorityId) as MAX_PRIORITY
 from message
  group by categoryId
 order by categoryId
) query2

where query1.MAX_PRIORITY = query2.MAX_PRIORITY

order by query1.categoryId

【讨论】:

【参考方案5】:
SELECT
    Messages.MessageID
    , Messages.CategoryID
    , Messages.Priority
    , Messages. MessageText
FROM
    Messages
    INNER JOIN
    (
        SELECT 
            CategoryID
            , MAX(Priority) AS Priority
        FROM 
            Messages
        GROUP BY
            CategoryID
    ) AS MaxResults
    ON
        (
            Messages.CategoryID = MaxResults.CategoryID
            AND
            Messages.Priority = MaxResults.Priority
        )

看起来这与上面给出的答案基本相同......具有相同的警告。

虽然这个可以马上工作。

【讨论】:

【参考方案6】:

这更短,更容易阅读 (imo)。

select ms.*
from 
  messages ms
 ,(
  select ms1.categoryid, max(ms1.priority) as max_priority
  from messages ms1
  group by ms1.categoryid
  ) tmp
where ms.categoryid = tmp.categoryid
  and ms.priority = tmp.max_priority;

【讨论】:

【参考方案7】:
SELECT DISTINCT CategoryId,PS.Priority,MessageID,MessageText
FROM Priority_Scene PS
JOIN (SELECT MAX(Priority) AS Priority FROM Priority_Scene GROUP BY CategoryId) A
ON A.Priority   = PS.Priority

【讨论】:

如果同一类别有两个相同且***别的优先级,那么它也将起作用【参考方案8】:

我的排名还不够高,无法发表评论,所以我想添加到 cfeduke 的解决方案中:

SELECT
    highest_priority_messages.*
FROM
(
    SELECT
    m.MessageID
    , m.CategoryID
    , m.Priority
    , m.MessageText
    , Rank() OVER 
        (PARTITION BY m.CategoryID ORDER BY m.Priority DESC, m.MessageID DESC) AS p_rank
    FROM [Message] m
    GROUP BY 
        m.CategoryID 
        , m.Priority
        , m.MessageID
        , m.MessageText
) highest_priority_messages
WHERE 
    p_rank = 1

如果您添加另一个具有优先级 3 的 CategoryID 100 消息,原始解决方案将带回 2 行,通过添加另一个订单条件,我们消除了两个项目排名相同的机会。

这是我为了测试而插入的行的副本。

insert into [Message] (MessageID, CategoryID, Priority, MessageText)
select 8, 100, 3, 'Error #976-2 occurred'

【讨论】:

以上是关于半棘手的 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

python 更棘手的SQL查询

棘手的 sql 查询 - 寻找替代供应商(关系部门)

SQL左反半连接等效查询

在 SQL 子查询中使用多个表进行 Oracle 半联接

SQL数据库内表太多,查询一次要半个多小时,如何优化?

SQL:查找锁定日期的棘手问题