努力寻找正确的 WHERE 子句

Struggling to find the right WHERE clause

我正在努力处理 SQL 查询,我需要您的帮助。老实说,我开始怀疑我想要实现的目标是否可以像我目前所做的那样完成,但也许你们的集体头脑可以想出比我更好的解决方案,并证明我在开始(或者我完全错了,我应该从头开始)。

数据集

一行有4个重要字段:ItemIDItemPriorityGroup。这些字段包含唯一有价值的信息,即最后将显示的信息。

因为我使用的是 SQL Server 2008,所以我无法访问 LAGLEAD 函数,所以我需要模拟它们(或者至少,我这样做是因为我认为它对我有用,但我不再那么确定了)。为获得此结果,我使用了 this article from SQLscope 中的代码,它为您提供了 LAGLEAD 等价物,我将其限制为具有相同 ItemID 的一组行。这会向我的数据集添加 7 个新的 functional 列:RnRnDiv2RnPlus1Div2PreviousPriorityNextPriorityPreviousGroupNextGroup.

ItemID      | Item      | Priority  | Group     | Rn        | RnDiv2    | RnPlus1Div2   | PreviousPriority  | NextPriority  | PreviousGroup     | NextGroup 
--------    | -------   | --------  | -------   | -----     | ------    | -----------   | ----------------  | ------------  | -------------     | --------- 
16777397    | Item 1    | 5         | Group 1   | 1         | 0         | 1             | NULL              | NULL          | NULL              | NULL      
16777403    | Item 2    | 5         | Group 2   | 1         | 0         | 1             | NULL              | 5             | NULL              | Group 2   
16777403    | Item 2    | 10        | Group 2   | 2         | 1         | 1             | 5                 | NULL          | Group 2           | NULL      
16777429    | Item 3    | 1000      | Group 3   | 1         | 0         | 1             | NULL              | NULL          | NULL              | NULL      
16777430    | Item 4    | 5         | Group 1   | 1         | 0         | 1             | NULL              | NULL          | NULL              | NULL      
16777454    | Item 5    | 5         | Group 4   | 1         | 0         | 1             | NULL              | NULL          | NULL              | NULL      
16777455    | Item 6    | 5         | Group 5   | 1         | 0         | 1             | NULL              | NULL          | NULL              | NULL      
16777459    | Item 6    | 5         | Group 6   | 1         | 0         | 1             | NULL              | NULL          | NULL              | NULL      
16777468    | Item 8    | 5         | Group 7   | 1         | 0         | 1             | NULL              | NULL          | NULL              | NULL      
16777479    | Item 9    | 5         | Group 4   | 1         | 0         | 1             | NULL              | NULL          | NULL              | NULL      
16777481    | Item 10   | 5         | Group 4   | 1         | 0         | 1             | NULL              | NULL          | NULL              | NULL      
16777496    | Item 11   | 5         | Group 6   | 1         | 0         | 1             | NULL              | NULL          | NULL              | NULL      
16777514    | Item 12   | 5         | Group 4   | 1         | 0         | 1             | NULL              | NULL          | NULL              | NULL      
16777518    | Item 13   | 5         | Group 8   | 1         | 0         | 1             | NULL              | 10            | NULL              | Group 8   
16777518    | Item 13   | 10        | Group 8   | 2         | 1         | 1             | 5                 | 100           | Group 8           | Group 1   
16777518    | Item 13   | 100       | Group 1   | 3         | 1         | 2             | 10                | NULL          | Group 8           | NULL      
16777520    | Item 14   | 5         | Group 9   | 1         | 0         | 1             | NULL              | NULL          | NULL              | NULL      

问题

我的 SQL 查询中的问题是 WHERE 子句。我将始终根据 Group 列过滤行。但也有一些微妙之处。无论 GroupItem 的成员数量是多少,我都希望它根据这些标准出现在 一个且只有一个 Group 中:

  1. 如果 Item 在同一个 Group 中出现不止一次,只有最低 priority 的行应该被 return 编辑。如果 Item 在同一个 Group 中出现多次,但具有相同的 Priority,则只应保留第一次出现的 例如:对于 Item 2,只有 Priority 值为 5 的行应该 returned;
  2. 如果 Item 出现在 Group 中,但也出现在另一个具有最低 PriorityGroup 中,则不应显示。 示例: Group 1 被选为过滤器。 Item 1 应该显示,但 Item 13 不应该显示,因为它也出现在 Group 8 中,但 Priority 较低(Item 13 只会出现在 Group 8).

请注意,这只是一个示例。我的真实数据集有 3000 多行,其他一些情况可能是我没有在示例中列出的。

尝试失败

正如我所说,WHERE 子句中有一个常量,那就是 Group 过滤。

我会继续尝试找到好的 WHERE 子句,但我觉得我的整个方法都是错误的。当同一个 Item 有两个以上的条目时,我不知道如何解决问题。值得注意的是,此查询用于 SSRS 报告,因此我可以使用自定义代码来解析数据集并过滤行(使用 tables 可能有助于解决包含两个以上条目的 Items 的问题)。但是,如果这里有 SQL 天才提供可行的解决方案,那就太好了。

PS : 如果有人知道如何解决这个 table 并且可以向我解释,给他额外的饼干。 :D

编辑:

这是我目前正在使用的修改后的查询。我会考虑使用@Yellowbedwetter 的最新查询,因为它看起来更可靠。

SELECT * 
  FROM (SELECT ItemID,
               Item,
               Priority,
               Group_,
               MIN(Priority) OVER
                 ( PARTITION BY item
                 ) AS interItem_MinPriority
          FROM (SELECT ItemID,
                       Item,
                       Priority,
                       Group_,
                       ROW_NUMBER() OVER
                         ( PARTITION BY Item
                               ORDER BY Priority ASC
                         ) AS interGrp_Rank
                  FROM Test_Table 
               ) AS TMP
         WHERE interGrp_Rank = 1 -- Exclude all records with the same item/group, but higher priority.
       ) AS TMP2
 WHERE Priority = interItem_MinPriority; -- Exclude which aren't the lowest priority across groups.

没试过,但是类似..`select max(priority) as mp ..... From ... Where group = 'group1' and mp not in (select max(priority).... from ... Where group <> 'group1'

抱歉打字,我 phone 没戴眼镜 :)

如果我正确理解问题,这应该有效

SELECT * 
  FROM (SELECT ItemID,
               Item,
               Priority,
               Group_,
               MIN(Priority) OVER
                 ( PARTITION BY item
                 ) AS interItem_MinPriority
          FROM (SELECT ItemID,
                       Item,
                       Priority,
                       Group_,
                       ROW_NUMBER() OVER
                         ( PARTITION BY Item,
                                        Group_
                               ORDER BY Priority ASC
                         ) AS interGrp_Rank
                  FROM Test_Table 
               ) AS TMP
         WHERE interGrp_Rank = 1 -- Exclude all records with the same item/group, but higher priority.
       ) AS TMP2
 WHERE Priority = interItem_MinPriority; -- Exclude which aren't the lowest priority across groups.

我不知道你的 SQL 服务器版本是否支持 MIN() OVER()...,但如果不支持,你应该能够很容易地解决这个问题。

编辑: 处理平局。

WITH TEST_TABLE (ItemID, Item, Priority, Group_) AS 
 (
 SELECT '16777397','Item 1','5','Group 1' UNION 
 SELECT '16777403','Item 2','5','Group 2' UNION 
 SELECT '16777403','Item 2','10','Group 2' UNION 
 SELECT '16777429','Item 3','1000','Group 3' UNION 
 SELECT '16777430','Item 4','5','Group 1' UNION 
 SELECT '16777454','Item 5','5','Group 4' UNION 
 SELECT '16777455','Item 6','5','Group 5' UNION 
 SELECT '16777459','Item 6','5','Group 6' UNION 
 SELECT '16777468','Item 8','5','Group 7' UNION 
 SELECT '16777479','Item 9','5','Group 4' UNION 
 SELECT '16777481','Item 10','5','Group 4' UNION 
 SELECT '16777496','Item 11','5','Group 6' UNION 
 SELECT '16777514','Item 12','5','Group 4' UNION 
 SELECT '16777518','Item 13','5','Group 8' UNION 
 SELECT '16777518','Item 13','10','Group 8' UNION 
 SELECT '16777518','Item 13','100','Group 1' UNION 
 SELECT '16777520','Item 14','5','Group 9'
 ) 

 SELECT ItemID,
        Item,
        Priority,
        Group_
   FROM (SELECT ItemID,
                Item,
                Priority,
                Group_,
                ROW_NUMBER() OVER
                  ( PARTITION BY item
                        ORDER BY Group_ ASC -- or however you want to break the tie
                  ) AS grp_minPriority_TieBreak
           FROM (SELECT ItemID,
                        Item,
                        Priority,
                        Group_,
                        MIN(Priority) OVER
                          ( PARTITION BY item
                          ) AS interItem_MinPriority
                   FROM (SELECT ItemID,
                                Item,
                                Priority,
                                Group_,
                                ROW_NUMBER() OVER
                                  ( PARTITION BY Item,
                                                 Group_
                                        ORDER BY Priority ASC
                                  ) AS interGrp_Rank
                           FROM TEST_TABLE 
                        ) AS TMP
                  WHERE interGrp_Rank = 1 -- Exclude all records with the same item/group, but higher priority.
                ) AS TMP2
          WHERE Priority = interItem_MinPriority -- Exclude which aren't the lowest priority across groups.
       ) AS TMP2
  WHERE grp_minPriority_TieBreak = 1;

如果我理解你的问题

关于这些标准

  1. 如果该项目多次出现在同一个组中,则只 应返回优先级最低的行。示例:对于项目 2、只返回Priority值为5的行;

  2. 如果该项目出现在组中但也出现在另一个组中 优先级最低的组,不应显示。例子: 选择第 1 组作为过滤器。应显示项目 1,但项目 13 不应该,因为它也出现在第 8 组中,具有较低的 优先级(项目 13 只会出现在第 8 组中)。

我认为我们可以在不考虑item组的情况下使用每个item的最小优先级得到正确的结果,因为在上面的两种情况下我们都采用了item的最小优先级

所以下面的查询可能会有帮助。(我用你的样本数据测试过)

with minPriority as
(
select ItemID, Item, Priority , Group_,ROW_NUMBER() over(partition by ItemId order by priority )rn  from Test_table 
)
select * from minPriority where rn=1