如何在SQL中的一个特定行中找到最少的非空列?

时间:2010-06-20 06:01:56

标签: sql mysql

我试图找到同一个表中一行的两列中的最小数字,但需要注意的是其中一列可能在某一行中为空。如果其中一列为null,我希望为该行返回的另一列中的值,因为在这种情况下,这是最低的非空列。如果我在MySQL 5.1中使用least()函数:

select least(1,null)

这会返回null,这不是我想要的。在这种情况下,我需要查询返回1.

我已经能够通过此查询获得我想要的结果:

select least(coalesce(col1, col2)) , coalesce(col2,col1))

只要col1和col2都不为null,每个coalesce语句将返回一个数字,而least()处理找到最低值。

有更简单/更快的方法吗?我在这个例子中使用MySQL,但欢迎使用一般的解决方案。

8 个答案:

答案 0 :(得分:15)

不幸的是(对于你的情况)在MySQL 5.0.13(http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_least)中改变了LEAST的行为 - 只有当所有参数都为NULL时它才会返回NULL。

此更改甚至被报告为错误:http://bugs.mysql.com/bug.php?id=15610 但修复只针对MySQL文档,解释了新行为和兼容性中断。

您的解决方案是推荐的解决方法之一。另一个可以使用IF运算符:

SELECT IF(Col1 IS NULL OR Col2 IS NULL, COALESCE(Col1, Col2), LEAST(Col1,Col2))

答案 1 :(得分:4)

根据您所有值为null的角落情况,我会选择更具可读性的语法(如果下面有两列,则更容易解决!)

SELECT LEAST( IFNULL(5, ~0 >> 1), IFNULL(10, ~0 >> 1) ) AS least_date;
-- Returns: 5

SELECT LEAST( IFNULL(null, ~0 >> 1), IFNULL(10, ~0 >> 1) ) AS least_date;
-- Returns: 10

SELECT LEAST( IFNULL(5, ~0 >> 1), IFNULL(null, ~0 >> 1) ) AS least_date;
-- Returns: 5

SELECT LEAST( IFNULL(null, ~0 >> 1), IFNULL(null, ~0 >> 1)) AS least_date
-- Returns: @MAX_VALUE (If you need to use it as default value)

SET @MAX_VALUE=~0 >> 1;
SELECT LEAST( IFNULL(null, @MAX_VALUE), IFNULL(null, @MAX_VALUE)) AS least_date;
-- Returns: @MAX_VALUE (If you need to use it as default value). Variables just makes it more readable!

SET @MAX_VALUE=~0 >> 1;
SELECT NULLIF(
    LEAST( IFNULL(null, @MAX_VALUE), IFNULL(null,@MAX_VALUE)),
    @MAX_VALUE
) AS least_date;
-- Returns: NULL

如果是

,这是我的首选方式
  • 您可以确保至少有一列不能NULL
  • 在极端情况下(所有列都是NULL)您希望非空的默认值大于任何可能的值或可以限制在某个阈值
  • 您可以处理变量以使此声明更具可读性

如果您质疑自己~0 >> 1的含义: 它只是一个简短的说法"给我最多的可用数量"。另见:https://stackoverflow.com/a/2679152/2427579

更好的是,如果您只有两列,则可以使用:

SELECT LEAST( IFNULL(@column1, @column2), IFNULL(@column2, @column1) ) AS least_date;
-- Returns: NULL (if both columns are null) or the least value

答案 2 :(得分:3)

这可能会更好一些(可能必须转换为相应的MySql语法):

SELECT
  CASE
    WHEN Col1 IS NULL THEN Col2
    WHEN Col2 IS NULL THEN Col1
    ELSE Least(Col1, Col2)
  END

另一种选择(虽然可能比较慢,但值得一试):

SELECT Col1
WHERE Col2 IS NULL
UNION
SELECT Col2
WHERE Col1 IS NULL
UNION
SELECT least(Col1, Col2)
WHERE Col1 IS NOT NULL AND Col2 IS NOT NULL

答案 3 :(得分:1)

为什么不将一列的值设置为等于另一列的值?

SELECT LEAST(IFNULL(COL1, COL2), IFNULL(COL2, COL1));

使用上面的代码,除非两者都为null,否则将忽略null值。

e.g。

COL1 = NULL,COL2 = 5

LEAST(IFNULL(NULL, 5), IFNULL(5, NULL)) -> LEAST(5, 5) -> 5

COL1 = 3,COL2 = NULL

LEAST(IFNULL(3, NULL), IFNULL(NULL, 3)) -> LEAST(3, 3) -> 3

COL1 = NULL,COL2 = NULL

LEAST(IFNULL(NULL, NULL), IFNULL(NULL, NULL)) -> LEAST(NULL, NULL) -> NULL

答案 4 :(得分:0)

选择 MIN(LEAST(COALESCE(COL1,COL2),COALESCE(COL2,CO1))) COL1不是空的     AND COL2不是空的;

答案 5 :(得分:0)

这是我解决的方法:

select coalesce(least(col1, col2), col1, col2)

如果一个值为NULL,则查询将返回第一个非NULL值。如果两个值都可以为NULL,则甚至可以将默认值添加为最后一个参数。

答案 6 :(得分:0)

我创建了一个处理任意数量日期的函数,方法是使用分隔符(CONCAT_WS)将它们连接起来作为该函数的第一个参数。

CONCAT_WS除了动态数量的参数外,还将删除所有NULL日期;)

该函数接受两个参数:

  • 分隔符将日期字符串分隔为TEXT
  • 分隔符为TEXT(与CONCAT_WS !!上使用的分隔符相同)-如果仅在CONCAT_WS上使用首选分隔符,则可以将其删除。
CREATE FUNCTION `min_date`(`dates` TEXT, `delim` VARCHAR(10)) RETURNS DATE NO SQL DETERMINISTIC
BEGIN

DECLARE `result` DATE DEFAULT NULL;

DECLARE `count` TINYINT DEFAULT 0;
DECLARE `temp` DATE DEFAULT NULL;

IF `delim` IS NULL THEN SET `delim` = ','; END IF;

IF `dates` IS NOT NULL AND CHAR_LENGTH(`dates`) > 0 THEN 
  SET `count` = LENGTH(`dates`) - LENGTH(REPLACE(`dates`, `delim`, SPACE(CHAR_LENGTH(`delim`) - 1)));

  WHILE `count` >= 0 DO
    SET `temp` = SUBSTRING_INDEX(SUBSTRING_INDEX(`dates`, `delim`, `count` + 1), `delim`, -1);

    IF `result` IS NULL OR `result` > `temp` THEN SET `result` = `temp`; END IF;

    SET `count` = `count` - 1;
  END WHILE;
END IF;

RETURN `result`;

END

然后,您可以在日期字段的任何组合中使用或作为静态字符串使用(只要有效日期或NULL):

SELECT min_date(CONCAT_WS(',', `date_column_1`, NULL, '2019-03-04', `date_column_2`), ',') AS `min_date`

答案 7 :(得分:0)

下面是一个简单的解决方案。

如果要查找最小的非空值,则可以将IFNULL与第二个参数用作“ INT限制”

ORDER BY LEAST(
    IFNULL(properties.sale_value, 2147483647),
    IFNULL(properties.rental_value, 2147483647),
    IFNULL(properties.daily_rental_value, 2147483647)
) ASC

如果要查找最大的非null值,则可以使用IFNULL,第二个参数为1(或者小于极限的第一个负值,如果不知道,请使用负整数限制)

ORDER BY GREATEST(
    IFNULL(properties.sale_value, 1),
    IFNULL(properties.rental_value, 1),
    IFNULL(properties.daily_rental_value, 1)
) ASC