sql根据条件第2部分选择最小值或最大值

时间:2015-08-07 15:16:40

标签: sql sql-server tsql sql-server-2012

这篇文章是继续发布另一篇帖子sql select min or max based on condition

的问题

我正试图根据各种条件获得一排。

场景1 - 获得最高行,如果没有针对具有(setup + processtime> 0)的小时。

场景2 - 如果有几个小时(如本例所示)显示此号码后的下一个操作(oprnum)。 (在prodroute中为60)。

查询需要在CTE中工作,因为它是更大查询的一部分。

    CREATE TABLE ProdRoute
    ([ProdId] varchar(10), [OprNum] int, [SetupTime] int, [ProcessTime] numeric)
;

INSERT INTO ProdRoute
    ([ProdId], [OprNum], [SetupTime], [ProcessTime])
VALUES
    ('12M0004893', 12, 0.7700000000000000, 1.2500000000000000),
    ('12M0004893', 12, 0.0000000000000000, 0.0000000000000000),
    ('12M0004893', 40, 0.0800000000000000, 0.4000000000000000),
    ('12M0004893', 50, 0.0400000000000000, 2.8000000000000000),
    ('12M0004893', 50, 0.0000000000000000, 0.0000000000000000),
    ('12M0004893', 60, 0.0000000000000000, 0.6100000000000000),
    ('12M0004893', 60, 0.0000000000000000, 0.0000000000000000),
    ('12M0004893', 70, 0.0000000000000000, 1.2900000000000000),
    ('12M0004893', 70, 0.0000000000000000, 0.0000000000000000),
    ('12M0004893', 75, 0.0000000000000000, 3.8700000000000000),
    ('12M0004893', 75, 0.0000000000000000, 0.0000000000000000),
    ('12M0004893', 80, 0.0000000000000000, 0.5500000000000000),
('12M0003571', 3, 0.8900000000000000, 0.0000000000000000),
    ('12M0003571', 3, 0.0000000000000000, 0.0000000000000000),
    ('12M0003571', 7, 1.0000000000000000, 0.0000000000000000),
    ('12M0003571', 10, 0.3000000000000000, 0.3000000000000000),
    ('12M0003571', 10, 0.0000000000000000, 0.0000000000000000),
    ('12M0003571', 20, 0.0700000000000000, 0.1000000000000000),
    ('12M0003571', 20, 0.0000000000000000, 0.0000000000000000),
    ('12M0003571', 30, 0.0000000000000000, 0.0000000000000000),
    ('12M0003571', 40, 0.0000000000000000, 0.0000000000000000),
    ('12M0003571', 50, 0.0000000000000000, 0.0000000000000000),
    ('12M0003571', 60, 0.0000000000000000, 0.0000000000000000),
    ('12M0003571', 60, 0.0000000000000000, 0.0000000000000000),
    ('12M0003571', 70, 0.0700000000000000, 0.1500000000000000),
    ('12M0003571', 70, 0.0000000000000000, 0.0000000000000000)
;

CREATE TABLE ProdRouteTran
    ([ProdID] varchar(10), [MaxOpCompleted] int, [Hours] numeric)
;

INSERT INTO ProdRouteTran
    ([ProdID], [MaxOpCompleted], [Hours])
VALUES
    ('12M0004893', 50, 1.7800000000000000),
('12M0003571', 70, 1.2660000000000000)
;

预期产出:

ProdId  OprNum
12M0004893  60

ProdId  OprNum
12M0003571  70

6 个答案:

答案 0 :(得分:8)

基于新数据和对问答的最后评论,这里有更新的查询和小提琴:http://sqlfiddle.com/#!6/87e2f/2

  嘿,我找到了一个不起作用的例子...... orderID' 12M0003381' ...   我已经为你的小提琴添加了数据。我希望将操作70视为   这是设置或处理时间的最后一次操作......谢谢!

select prodid, ISNULL(MAX(weighted_value),MIN(oprnum)) as value from
(           
            select 
                a.prodid,
                a.oprnum,
                ISNULL(LEAD(a.oprnum,1) OVER(Partition by a.prodID  ORDER by a.oprnum asc),a.oprnum)  * 
                MAX(case 
                    when ISNULL([Hours], 0) >= (setupTime + ProcessTime) AND (SetupTime + ProcessTime ) > 0 
                    then 1
                    else NULL
                    end) as weighted_value
            from temp1 a LEFT JOIN temp4 b 
                    ON a.OprNum = b.OPRNUM
                    AND a.ProdID = b.ProdId
            group by a.prodid,a.oprnum
) t
group by prodid

以下查询更改的说明:

对查询进行的唯一更改是使用以下语法处理NULL的{​​{1}}值

weighted_value

有问题的部分是内部查询,当没有group by子句运行时,会显示在用户添加的边界情况下发生的情况。

enter image description here

(请参阅此处的小提琴:http://sqlfiddle.com/#!6/87e2f/3

如果没有空值处理,我们有一个ISNULL(LEAD(a.oprnum,1) OVER(Partition by a.prodID ORDER by a.oprnum asc),a.oprnum) NULL子句后导致enter image description here下面的结构

(请参阅此处的小提琴:http://sqlfiddle.com/#!6/87e2f/5

正如您所看到的那样,将group by的LEAD值归为prodid : 12M0003381, oprnum:70而不是NULL(因为分组7070应该给{{ 1}})。

如果在分组查询/表格上计算NULL,这是合理的,这实际上就是这里发生的事情。

在这种情况下,70函数不会返回最后一行分区的任何数据。这是边界情况,必须使用LEAD正确处理。

我认为最后一行的LEAD ISNULL值应更正为当前行的LEAD值。

以下旧答案:

  

所以我尝试了,我发布了小提琴链接   http://sqlfiddle.com/#!6/e965c/1

oprnum

答案 1 :(得分:6)

这不是我写过的最漂亮的东西,但它有效。我还用另外的数据对另一个小提琴进行了测试。

修改以满足新要求。

SELECT
    *
FROM
    (
        SELECT
            A.ProdID,
            MIN(A.OprNum) AS 'OprNum'
        FROM
            #ProdRoute AS A
            JOIN 
                (
                    SELECT
                        ProdID,
                        MAX(MaxOpCompleted) AS 'OprNum'
                    FROM
                        #ProdRouteTran
                    GROUP BY
                        ProdID
                ) AS B
                ON A.ProdId = B.ProdId AND A.OprNum > B.OprNum
        GROUP BY
            A.ProdID
    ) AS [HoursA]
UNION ALL
SELECT
    *
FROM
    (
        SELECT
            DISTINCT
            A.ProdID,
            B.OprNum
        FROM
            #ProdRoute AS A
            JOIN 
                (
                    SELECT
                        ProdID,
                        MAX(MaxOpCompleted) AS 'OprNum'
                    FROM
                        #ProdRouteTran
                    GROUP BY
                        ProdID
                ) AS B
                ON A.ProdId = B.ProdId AND A.OprNum = B.OprNum
                    AND B.OprNum = (SELECT MAX(OprNum) FROM #ProdRoute WHERE ProdId = A.ProdId)
    ) AS [HoursB]
UNION ALL
SELECT
    *
FROM
    (
        SELECT
            ProdId,
            MIN(OprNum) AS 'OprNum'
        FROM
            #ProdRoute
        WHERE
            ProdId NOT IN 
                (SELECT ProdId FROM #ProdRouteTran)
            AND (SetupTime <> 0 OR ProcessTime <> 0)
        GROUP BY
            ProdId
    ) AS [NoHoursA]
UNION ALL
SELECT
    *
FROM
    (
        SELECT
            ProdId,
            MIN(OprNum) AS 'OprNum'
        FROM
            #ProdRoute
        WHERE
            ProdId NOT IN 
                (SELECT ProdId FROM #ProdRouteTran)
        GROUP BY
            ProdId
        HAVING
            SUM(SetupTime) = 0 AND SUM(ProcessTime) = 0
    ) AS [NoHoursB]

答案 2 :(得分:6)

我不确定我理解你的问题,但这是我的尝试:

SELECT
    pr.ProdId,
    CASE 
        WHEN SUM(SetupTime) + SUM(ProcessTime) > 0 THEN MAX(x.OprNum)
        ELSE MAX(pr.OprNum)
    END
FROM ProdRoute pr
INNER JOIN (
    SELECT ProdID, MAX(MaxOpCompleted) AS OprNum
    FROM ProdRouteTran
    GROUP BY ProdID
)prt
    ON prt.ProdId = pr.ProdID
    AND prt.OprNum = pr.OprNum
OUTER APPLY(
    SELECT TOP 1 OprNum FROM ProdRoute 
    WHERE 
        ProdId = pr.ProdId
        AND OprNum > pr.OprNum
    ORDER BY OprNum
)x
GROUP BY pr.ProdId
ORDER BY pr.ProdId

答案 3 :(得分:6)

试试这个 -

-- display the next operation, if condition match
SELECT do_exists.ProdId, do_exists.OprNum
FROM ProdRoute pr
INNER JOIN ProdRouteTran prt 
    ON prt.ProdId = pr.ProdId 
    AND pr.OprNum = prt.MaxOpCompleted 
    AND (pr.SetupTime + pr.ProcessTime) > 0
OUTER APPLY (
    SELECT TOP(1) pr.* 
    FROM ProdRoute pr
    WHERE prt.ProdID = pr.ProdId
    AND   pr.OprNum > prt.MaxOpCompleted
    ORDER BY pr.OprNum
) do_exists


UNION ALL

-- display the max operation, if matching data is not found in ProdRoute. 
---- Matching Data not found - 1) There is entry in ProdRoute for particular ProdId but hours is not present
----                           2) There is no entry in ProdRoute for a particular ProdId    
SELECT pr.ProdId, MAX(pr.OprNum) OprNum
FROM ProdRoute pr
LEFT JOIN (
    SELECT pr.ProdId
    FROM ProdRoute pr
    INNER JOIN ProdRouteTran prt 
        ON prt.ProdId = pr.ProdId 
        AND pr.OprNum = prt.MaxOpCompleted 
        AND (pr.SetupTime + pr.ProcessTime) > 0
) pr_ex ON pr_ex.ProdId = pr.ProdId
WHERE pr_ex.ProdId IS NULL 
GROUP BY pr.ProdId

答案 4 :(得分:6)

我使用此SQL Fiddle获得了正确的结果。

但我不确定我完全理解nothing >0案例。所有案例的更多数据都可能有用。

; With data as (
    Select r.ProdId
    , opr = case when h > 0 then  isnull(min(p.OprNum), r.OprNum) else  max(p.OprNum) end
    From (
        Select pr.ProdId, pr.OprNum, h = max(pr.SetupTime + pr.ProcessTime)     
        From ProdRoute as pr
        Inner Join ProdRouteTran as prt on pr.ProdId = prt.ProdID and pr.OprNum = prt.MaxOpCompleted
        Group By pr.ProdId, pr.OprNum
    ) as r
    left join ProdRoute as p on p.ProdId = r.ProdId and p.OprNum > r.OprNum
    Group By r.ProdId, r.OprNum, r.h
)
Select * From data

答案 5 :(得分:5)

我不完全确定我是否明白你想要做什么,但这可能是等同的吗?

SELECT
   t.ProdId,
   CASE WHEN r.OprNum IS NULL THEN t.MaxOpCompleted ELSE r.OprNum END AS OprNum
FROM
   ProdRouteTran t
   LEFT JOIN
      ProdRoute r
   ON
      r.ProdId = t.ProdId AND r.SetupTime + r.ProcessTime > 0 AND
      r.OprNum > t.MaxOpCompleted AND NOT EXISTS(
         SELECT * FROM ProdRoute p WHERE p.ProdId = t.ProdId AND
            p.OprNum > t.MaxOpCompleted AND p.OprNum < r.OprNum)