如何在返回可变行数时在MySQL中编写if else-if else查询

时间:2011-10-06 20:20:45

标签: mysql sql

我在表格中有三列:

score  status  No.
1,     2,      1
0,     1,      2
0,     0,      1

我需要这个,写一个C#样式的伪SQL:

rows = empty;

rows = "SELECT * FROM `table` WHERE score = 1"
if (rows.Count > 0) //at least one row
    return rows;

rows = "SELECT * FROM `table` WHERE status = 2"
if (rows.Count > 1) //more than one row 
    return row with MAX(No.) from rows; //ie MAX(No.) where status = 2

 return rows;

我希望我能说清楚。简而言之,从我的表记录中选择得分= 1,如果没有这样的记录,则返回status = 2的记录,如果有多个记录,状态= 2,则返回最大值的记录如果状态= 2,则为否(如果状态= 2,则根本没有记录,则返回空)。

如何在一个查询中编写它?对我来说这应该是一个很好的学习经历。否则我知道要分解成较小的查询并运行每个查询。我现在不能使用存储过程..

编辑:实际上我的查询会有一些WHERE子句但在两个条件中都相同,这就是我省略它的原因。因此,关于第一个条件,现在只返回一条记录。那就是SELECT * FROM table WHERE score = 1现在只返回一行。并且我需要/我会接受答案,这也提供了这样的解决方案。但关键是你永远不知道,未来可能会有一些设计更改,可能会有更多行score = 1。这就是为什么我选择记录而不是记录。但理想情况下,业务逻辑是拥有score = 1的所有记录。现在,记录会做。如果只返回一行并且我的队友可以轻松地同化代码,我只是觉得查询会简单得多。

最终更新:谢谢大家,你们非常善良:)很多答案都很顺利,选择一个真的很令人生畏。我对答案的发现:

  1. 始终有效的答案:@GordonLinoff's@ZaneBiens's@ZaneBien's another@Scen's@JulienCh.'s(最后3个基本相同,但是但我不完全清楚这些是如何工作的:))

  2. 仅在第一个条件score = 1仅返回一行时才有效的答案:@HannoBinder's@ShlomiNoach's@DaniellePaquette-Harvey's。目前,我会坚持使用@ Danielle's(这非常简单),如果需要有多行,我会后来还原。

  3. 其余的答案,我无法测试,因为它们不是非常具体或与MySQL无关。

  4. @MatthewPK's在上下文中不合适。

  5. 奖励赏金并接受答案很难得到如此多的正确答案。我之所以选择this one,因为我觉得它可能更有效率和可读性,但我会接受@ Scen的answer简单明了。

12 个答案:

答案 0 :(得分:5)

这看起来很难,因为在这两种情况下你返回的行数可变。

以下查询组合了所有可能的行,然后检查是否存在第一个类型以查看应返回的类型。

select *
from ((SELECT 'SCORE' as matchtype, t.*
       FROM `table` t
       WHERE score = 1
      ) union all
      (SELECT 'STATUS' as matchtype, t.*
       FROM `table` t join
            (select max(`No.`) as maxno
             from `table` 
             WHERE status = 2
           ) tsum
           on t.`No.` = tsum.maxno
       WHERE status = 2
      )
     ) t cross join
     (select count(*) as cnt
      from `table`
      where score = 1
     ) const
where matchtype = (case when const.cnt > 0 then 'SCORE' else 'STATUS' end)

注意:返回集包含MatchType。要摆脱这种情况,您需要包含所需列的完整列表。

答案 1 :(得分:4)

此查询将解决您的问题:

SELECT *
  FROM `your_table`
WHERE
  status = 2 OR score = 1 
ORDER BY
  if(score = 1, 1, 0) DESC, 
  if(status = 2, 1, 0) DESC,
  `No.` DESC
LIMIT 1;

与其他答案相反:

  • 它不会假设列No.
  • 的正值
  • 这是MySQL语法(您已标记为mysql
但是,请随意向KSiimson表达信誉/声誉,KSiimson布置了正确的道路。不幸的是,他的查询有两个错误。

另请注意,所述查询无法利用ORDER BY子句上的任何索引,因为我们使用函数包装列。由于FROM clause条件,OR不太可能使用索引。

答案 2 :(得分:3)

这是一个更有效的解决方案,应该适合您:

SELECT * FROM tbl WHERE score = 1 

UNION ALL

(
    SELECT a.* 
    FROM tbl a
    INNER JOIN
    (
        SELECT COUNT(1) AS s1exists FROM tbl WHERE score = 1
    ) b ON b.s1exists = 0
    WHERE a.status = 2
    ORDER BY a.no DESC
    LIMIT 1
)

故障:

此查询的工作原理是SELECT中的第一个UNION返回score = 1的所有行。第二个SELECT返回最大行(基于no = 2 AND ,其中status = 1的行没有。是否只有一行或多行score = 2,两者的最大行相同。

这满足您指定的所有条件:


  • 存在status = 1 的一行或多行:首先score返回所有这些行;第二个SELECT返回空(即使有SELECT = 2的行。)

  • status = 1且score = 2 的行中没有行; 首先status返回空;第二个SELECT返回一行(与最大行相同的相同)。

  • SELECT = 1且且score = 2 的行数不止一行时,不存在任何行:首先status返回空;第二个SELECT返回SELECT = 2。
  • 的最大行

  • status = 1或score = 2 的所有行都不存在:第一行不返回任何内容;第二行也不返回任何内容,呈现空结果。

答案 3 :(得分:2)

完全未经测试,很可能非常低效,但可能有效:

SELECT * FROM `table`
WHERE score=1

union all

SELECT * FROM `table`
WHERE status=2 
and NOT EXISTS (SELECT * FROM `table` WHERE score=1)
and `No.` = (
             SELECT max(`No.`) FROM `table`
             WHERE status=2 
            )

答案 4 :(得分:1)

假设No.中仅使用正整数:

select   * 
from     `table` 
where    status = 2 or score = 1 
order by if(score = 1, -2, if(status = 2, -1)), `No.` desc 
limit    1;

我做的是创建了一个表达式,行将按行排序。如果得分为1,则表达式将返回-2,仅当status为1时,表达式将返回-1。作为后备,我们按No.字段按降序排序。

请参阅Shlomi Noach的解决方案,以便在No.字段具有负值时有效。

答案 5 :(得分:1)

您可以从此查询中获取第一个结果:

-- If there is no row with Score 1, then the first SELECT will return nothing.
-- If there is a row with Score 1, then you will get it.
-- No row with Score 1. If there is one row with Status 2, then you will get it.
-- No row with Score 1. If there is more than one row with Status 2, then you will get the one with the highest No.

SELECT 1 AS ColumnOrder, Table.*
FROM   Table
WHERE  Score = 1
LIMIT  1
UNION ALL
SELECT   2 AS ColumnOrder, Table.* 
FROM     Table 
WHERE    Status=2 
ORDER BY ColumnOrder, No DESC
LIMIT    1 

答案 6 :(得分:1)

我离开了原来的答案,但效率不如下面那样:

/*
Query returns rows with score=1 when they exist or nothing
*/
select score, status, no from temp where score=1

UNION

/*
Query returns rows with status=2 when they exist and when there are no rows
 with score=1 or nothing otherwise.
*/
(

select t1.score, t1.status, t1.no from

( /* <subquery> */

(
select score, status, no from temp
where status=2 and (select max(score) from temp where score=1) is null
) as t1

join

(
select max(no) as maxNo from temp where status=2
) as t2

on t1.no=t2.maxNo

) /* </subquery> */

limit 1

)

您可以使用索引来提高效率。另外,我不确定条件语句中的查询(检查是否有任何得分为1的行)是否被执行一次或多次。为了安全起见,您可以将其替换为@c并添加以下行:

set @c:=(select max(score) from temp where score=1);

原始答案:

/*
Query returns rows with score=1 when they exist or nothing
*/
select score, status, no from temp where score=1

UNION

/*
Query returns nothing when there are rows with score=1,
 rows with status=2 when they exist and when there are no rows with score=1, or
 nothing otherwise.

Does this by adding weights to rows.
Rows with score=1 have greater weights than rows with status=2.
Joins rows with greatest weights to rows with status=2.
*/
( /* <query> */

select t.score, t.status, t.no from
( /* <joins> */

/* Gets greatest weight */
(
select max(if(score=1, 2, if(status=2, 1, -1))) as w
from temp
) as t1

join

/*  Joined on rows with status=2 using assigned weights */
(
select score, status, no, if(score=1, 2, if(status=2, 1, -2)) as w
from temp
where status=2
) as t

on t.w=t1.w

join

/*  Joined on rows with greatest No. for rows with status=2 */
(
select max(no) as maxNo
from temp
where status=2
) as t2

on t.no=t2.maxNo

) limit 1 /* </joins> (Only returns one row with max No.) */

) /* </query> */

答案 7 :(得分:1)

我不能(但是?)只在一个查询中看到问题的任何优雅解决方案。

此外,我不确定你的实际问题是什么;您的示例代码似乎很清楚,但我发现很难从

中推断出满足您要求的问题
  

我的查询将有一些WHERE子句,但两者都相同   条件

除此之外,我很惊讶到目前为止还没有人建议使用group by条款。 这里没有深入探讨MySQL的语法细节,但基本上是

select score, status, MAX(No) as No
from tbl 
group by score, status

将为每个“群组”提供恰好单一的记录,其中No的最大值为。

放入子查询可以改进上述select,例如

select *
from ( [*] ) as tbl
where
   (score = 1)
OR (score != 1 AND status = 2)
OR /* ... more conditions of the above type... */
order by /* ... */
limit 1

[*]代表上面group ed select语句

根据需要添加或删除order by score DESC, status DESC和/或limit 1子句的变体,您应该非常接近您需要的内容。

答案 8 :(得分:1)

IF EXISTS(Select * From `table` Where score = 1)
Begin
    Select * From `table` Where score = 1;
End
Else
Begin
    Select *
    From `table`
    Where status = 2
    Order by `No.` desc
    LIMIT 1
End

答案 9 :(得分:1)

这是我的other solution,但我将这一个作为另一个答案发布,因为它使用了完全不同的技术(WHERE子句中的条件条件),而不是使用UNION。< / p>

这避免了必须使用UNION运算符,这可能是您所追求的(尽管技术上使用两个语句中的UNION计为“一个”查询)。另一个优点是它与DBMS无关,并且不依赖于ORDER BY X / LIMIT 1,它主要是基于MySQL的技术来检索最小和最大行:

SELECT a.*
FROM tbl a
CROSS JOIN (SELECT COUNT(1) AS score1cnt FROM tbl WHERE score = 1) b
CROSS JOIN (SELECT MAX(no) AS maxno FROM tbl WHERE status = 2) c
WHERE
    CASE WHEN b.score1cnt > 0 THEN a.score = 1
         WHEN b.score1cnt = 0 AND c.maxno IS NOT NULL THEN a.status = 2 AND a.no = c.maxno
         ELSE 0
    END

你可以使用SQLFiddle Demo来解决这个问题,包括我的答案。

答案 10 :(得分:1)

Select *
From scores
Where score = 1
UNION
(
   Select *
   From scores
   Where status = 2 and not exists (Select * from scores where score = 1)
   Order By `No.` desc
   Limit 1
);

我将table命名为scores

SqlFiddle(查询一些假的架构,以便您可以看到结果): http://sqlfiddle.com/#!2/6aac2/1/0

编辑:编辑SQlFiddle以将假模式与问题中的模式匹配。

答案 11 :(得分:0)

SELECT SCORE, STATUS, NO from (
    SELECT * , RANK() OVER (PARTITION BY [STATUS] ORDER BY SCORE ASC, NO DESC) AS RANK
    FROM `table`
    WHERE  score = 1 OR [status] = 2 ) x
WHERE X.Rank = 1