20

我有以下选择语句来获取流的下一个预定项目。如果没有匹配的行,我希望它返回一个默认值。这是我正在使用的 SQL:

SELECT `file`
FROM `show`, `schedule` 
WHERE `channel` = 1
  AND `start_time` <= UNIX_TIMESTAMP() 
  AND `start_time` > UNIX_TIMESTAMP()-1800
  AND `show`.`id` = `schedule`.`file` 
ORDER BY `start_time`
DESC LIMIT 1

这应该抓取最近安排的项目,但如果它在查询前 30 分钟以上则不会。

但是,如果用户没有安排任何事情,我想要一个默认值,以便在流上实际播放一些东西。我尝试了以下方法:

SELECT COALESCE(`file`, 'default.webm')
FROM `show`, `schedule`...

SELECT IFNULL(`file`, 'default.webm')
FROM `show`, `schedule`

但是,如果没有找到任何行,它总是返回一个空结果。我怎样才能返回一个默认值呢?

4

3 回答 3

27

一种方法

SELECT IFNULL(MIN(`file`), 'default.webm') `file` 
  FROM `show`, `schedule` 
 WHERE `channel` = 1 AND `start_time` <= UNIX_TIMESTAMP() 
   AND `start_time` > UNIX_TIMESTAMP()-1800 AND `show`.`id` = `schedule`.`file` 
 ORDER BY `start_time` DESC LIMIT 1

由于您只返回一行,因此您可以在这种情况下使用聚合函数,以MIN()确保NULL在未选择任何记录时您将获得。然后IFNULL()COALESCE()将完成它的工作。

于 2013-03-10T05:37:43.577 回答
15

@peterm 的答案和这个答案旨在适应 SQL 逻辑,该逻辑将在结果集中返回最多一行。

他的答案旨在返回单行和单列。

我的答案旨在返回包含一列或多列的单行。

本质上,您可以UNION在第二个子句中使用硬编码值,该SELECT子句与第一个 SELECT 具有相同的列数。

对于 OP:

(
    SELECT `file`
    FROM `show`,
         `schedule` 
    WHERE `channel` = 1
      AND `start_time` BETWEEN UNIX_TIMESTAMP()-1799 AND UNIX_TIMESTAMP() 
      AND `show`.`id` = `schedule`.`file` 
    ORDER BY `start_time` DESC
    LIMIT 1
) UNION (
    SELECT 'default.webm'
)
ORDER BY file = 'default.webm'
LIMIT 1;

除非出现任何语法错误,否则结果集将提供一行,其中一列键为file.


作为一个更一般的例子:(DB-Fiddle Demo

(
    SELECT Col1,Col2,Col3
    FROM ExampleTable
    WHERE ID='1234'
) UNION (
    SELECT 'Def Val','none',''
)
ORDER BY Col1 = 'Def Val'
LIMIT 1;

结果:

  • 如果在第一个 SELECT 中没有找到任何行,则结果集将填充第二个 SELECT 中的值。作为数组的结果集将是:

    [['Col1' => 'Def Val', 'Col2' => 'none', 'Col3' => '']]
    
  • 如果在第一个 SELECT 中找到一行,则在结果集中提供第一个 SELECT 值,而省略第二个 SELECT 值。结果集将是:(请参阅我的演示链接)

    [['Col1' => 'A', 'Col2' => 'B', 'Col3' => 'C']]
    

*结果集中的关联键将由第一个 SELECT 查询中的列名/别名决定。

*后续的 SELECT 查询不需要指定列别名。

*UNION 不要求来自两个联合查询的列名相同。事实上,后续 SELECT 查询中的列名或数据源可能是任何东西(不同的列、函数调用等)。

(
    SELECT Col1,Col2,Col3
    FROM ExampleTable
    WHERE ID='1234'
) UNION (
    SELECT 'Def Val' AS `Fallback1`,
           'none'    AS `Fallback2`,
           ''        AS `Fallback3`
)
ORDER BY Col1 = 'Def Val'
LIMIT 1;

我的观点是,这很容易阅读,而且看起来不像是一个繁重的查询。


感谢@LudovicKuty 发现了一个关于 UNION 生成的行顺序的潜在错误。https://dev.mysql.com/doc/refman/8.0/en/union.html#union-order-by-limit要消除默认行在找到的行之前排序的可能性,请编写一个 ORDER BY 子句足够的逻辑来确保默认行总是在结果集中的后面排序。将有不同的 sql-dialect-specific 语法可用于识别默认行。在某些ORDER BY columnName = 'default value'情况下就足够了,其他人可能会要求IFor IIForCASE等​​。只要您构建逻辑以便默认返回一个真实的结果,那么 true 将被视为1并且 false 将被视为0- 当然1是在之后0升序排序时。

于 2016-01-13T04:51:28.303 回答
0

要处理更广泛的情况,您需要一些条件逻辑。这仅在 MySQL 中的存储过程中可用,因此您需要将此代码包装在一个过程中并调用它:

if exists (
       SELECT `file` FROM `show`, `schedule` 
       WHERE `channel` = 1 AND `start_time` <= UNIX_TIMESTAMP() 
       AND `start_time` > UNIX_TIMESTAMP()-1800 AND `show`.`id` = `schedule`.`file` 
) then
       SELECT `file` FROM `show`, `schedule` 
       WHERE `channel` = 1 AND `start_time` <= UNIX_TIMESTAMP() 
       AND `start_time` > UNIX_TIMESTAMP()-1800 AND `show`.`id` = `schedule`.`file` 
       ORDER BY `start_time` DESC LIMIT 1
; else
        select `DefaultValue` as `file`
; end if
于 2015-10-08T13:48:43.400 回答