【问题标题】:How to set a default row for a query that returns no rows?如何为不返回行的查询设置默认行?
【发布时间】:2008-11-12 22:44:01
【问题描述】:

如果表中不存在任何行,我需要知道如何返回默认行。最好的方法是什么?我只从这个特定的表中返回一个列来获取它的值。

编辑:这将是 SQL Server。

【问题讨论】:

  • 你用的是什么数据库服务器,SQL Server、Oracle、MYSQL?
  • 如果您使用的是 SQL Server,为什么要接受仅适用于 Oracle 的答案?

标签: sql sql-server


【解决方案1】:

Oracle 的一种方法:

SELECT val
FROM myTable
UNION ALL
SELECT 'DEFAULT'
FROM dual
WHERE NOT EXISTS (SELECT * FROM myTable)

或者在 Oracle 中:

SELECT NVL(MIN(val), 'DEFAULT')
FROM myTable

或者在 SqlServer 中:

SELECT ISNULL(MIN(val), 'DEFAULT')
FROM myTable

这些使用MIN() 在没有行时返回NULL 的事实。

【讨论】:

  • 在我的例子中,我使用了 MAX,因为我需要最大值。非常感谢!
【解决方案2】:

这个怎么样:

SELECT DEF.Rate, ACTUAL.Rate, COALESCE(ACTUAL.Rate, DEF.Rate) AS UseThisRate
FROM 
  (SELECT 0) DEF (Rate) -- This is your default rate
LEFT JOIN (
  select rate 
  from d_payment_index
  --WHERE 1=2   -- Uncomment this line to simulate a missing value

  --...HERE IF YOUR ACTUAL WHERE CLAUSE. Removed for testing purposes...
  --where fy = 2007
  -- and payment_year = 2008
  --  and program_id = 18
) ACTUAL (Rate) ON 1=1

结果

有效率存在

Rate        Rate        UseThisRate
----------- ----------- -----------
0           1           1

使用的默认费率

Rate        Rate        UseThisRate
----------- ----------- -----------
0           NULL        0

测试 DDL

CREATE TABLE d_payment_index (rate int NOT NULL)
INSERT INTO d_payment_index VALUES (1)

【讨论】:

    【解决方案3】:

    这个 sn-p 使用通用表表达式来减少冗余代码并提高可读性。这是约翰鲍曼答案的变体。

    语法适用于 SQL Server。

    WITH products AS (
                SELECT prod_name,
                       price
                  FROM Products_Table
                 WHERE prod_name LIKE '%foo%'
         ),
         defaults AS (
                SELECT '-' AS prod_name,
                       0   AS price
         )
    
    SELECT * FROM products
    UNION ALL
    SELECT * FROM defaults
     WHERE NOT EXISTS ( SELECT * FROM products );
    

    【讨论】:

      【解决方案4】:

      *SQL 解决方案

      假设您有一个包含主键“id”的评论表。

      SELECT * FROM review WHERE id = 1555
      UNION ALL
      SELECT * FROM review WHERE NOT EXISTS ( SELECT * FROM review where id = 1555 ) AND id = 1
      

      如果表没有 1555 id 的评论,则此查询将提供 id 1 的评论。

      【讨论】:

        【解决方案5】:

        假设在config_code 列上有一个具有唯一索引的表config

        CONFIG_CODE     PARAM1   PARAM2
        --------------- -------- --------
        default_config  def      000
        config1         abc      123
        config2         def      456
        

        此查询返回config1 值的行,因为它存在于表中:

        SELECT *
          FROM (SELECT *
                  FROM config
                 WHERE config_code = 'config1'
                    OR config_code = 'default_config'
                 ORDER BY CASE config_code WHEN 'default_config' THEN 999 ELSE 1 END)
         WHERE rownum = 1;
        
        CONFIG_CODE     PARAM1   PARAM2
        --------------- -------- --------
        config1         abc      123
        

        这个返回默认记录,因为config3在表中不存在:

        SELECT *
          FROM (SELECT *
                  FROM config
                 WHERE config_code = 'config3'
                    OR config_code = 'default_config'
                 ORDER BY CASE config_code WHEN 'default_config' THEN 999 ELSE 1 END)
         WHERE rownum = 1;
        
        CONFIG_CODE     PARAM1   PARAM2
        --------------- -------- --------
        default_config  def      000
        

        与其他解决方案相比,此解决方案仅查询表config 一次。

        【讨论】:

          【解决方案6】:

          如果您的基本查询预计只返回一行,那么您可以使用这个技巧:

          select NVL( MIN(rate), 0 ) AS rate 
          from d_payment_index
          where fy = 2007
            and payment_year = 2008
            and program_id = 18
          

          (Oracle 代码,不确定 NVL 是否适合 SQL Server。)

          【讨论】:

          • ISNULL 是相当于 NVL 的 SQL Server... :-)
          • 问题是当没有返回任何行时如何返回默认值。
          • 如果没有记录匹配where 子句,此查询仍可能产生 0 条记录。当记录存在且其值为 null 时,您的 NVL/ISNULL 只会将空值转换为 0。
          • 使用 MIN 函数意味着,如果没有符合条件的行,将返回具有 NULL 值的单行。 (至少,在 Oracle 中是这样——也许 SQL Server 是不同的。)
          • 如果有很多列,这会变得很乱。
          【解决方案7】:

          这将消除选择查询运行两次并提高性能:

          Declare @rate int
          
          select 
              @rate = rate 
          from 
              d_payment_index
          where 
              fy = 2007
              and payment_year = 2008
              and program_id = 18
          
          IF @@rowcount = 0
              Set @rate = 0
          
          Select @rate 'rate'
          

          【讨论】:

            【解决方案8】:

            我想通了,它也应该适用于其他系统。这是 WW 答案的变体。

            select rate 
            from d_payment_index
            where fy = 2007
              and payment_year = 2008
              and program_id = 18
            union
            select 0 as rate 
            from d_payment_index 
            where not exists( select rate 
                              from d_payment_index
                              where fy = 2007
                                and payment_year = 2008
                                and program_id = 18 )
            

            【讨论】:

            • 该解决方案的唯一问题是您运行了两次查找。另一种方法是将结果存储在变量中,并且仅在第一个查询的行数为零时才返回默认值。
            • 唯一的另一个问题是我在代码中运行它,因此最好使用单个语句。但是,是的,我同意你的看法。
            • 根据大局,您可能实际上需要一个 OUTER JOIN。如果这是在通过记录集的循环内,则可能有更好的方法。
            • 应该使用 UNION ALL,因为它比 UNION 快。 (仅当您关心不同的结果并且可能返回重复结果时才使用 UNION。)
            • UNION ALL 已注明。这确实是有道理的,在这种情况下,我得到的只是一列,一排。谢谢,海滩!
            【解决方案9】:

            一种使用从默认值到实际值的左连接的表扫描方法:

            CREATE TABLE [stackoverflow-285666] (k int, val varchar(255))
            
            INSERT  INTO [stackoverflow-285666]
            VALUES  (1, '1-1')
            INSERT  INTO [stackoverflow-285666]
            VALUES  (1, '1-2')
            INSERT  INTO [stackoverflow-285666]
            VALUES  (1, '1-3')
            INSERT  INTO [stackoverflow-285666]
            VALUES  (2, '2-1')
            INSERT  INTO [stackoverflow-285666]
            VALUES  (2, '2-2')
            
            DECLARE @k AS int
            SET @k = 0
            
            WHILE @k < 3
                BEGIN
                    SELECT  @k AS k
                           ,COALESCE(ActualValue, DefaultValue) AS [Value]
                    FROM    (
                             SELECT 'DefaultValue' AS DefaultValue
                            ) AS Defaults
                    LEFT JOIN (
                               SELECT   val AS ActualValue
                               FROM     [stackoverflow-285666]
                               WHERE    k = @k
                              ) AS [Values]
                            ON 1 = 1
            
                    SET @k = @k + 1
                END
            
            DROP TABLE [stackoverflow-285666]
            

            给出输出:

            k           Value
            ----------- ------------
            0           DefaultValue
            
            k           Value
            ----------- ------------
            1           1-1
            1           1-2
            1           1-3
            
            k           Value
            ----------- ------------
            2           2-1
            2           2-2
            

            【讨论】:

              【解决方案10】:

              您要返回整行吗?默认行是否需要具有默认值或可以是空行?您是否希望默认行与相关表具有相同的列结构?

              根据您的要求,您可以执行以下操作:

              1) 运行查询并将结果放入临时表(或表变量)中 2)检查临时表是否有结果 3) 如果不是,则通过执行类似于此的 select 语句返回一个空行(在 SQL Server 中):

              select '' as columnA, '' as columnB, '' as columnC from #tempTable
              

              其中 columnA、columnB 和 columnC 是您的实际列名。

              【讨论】:

                【解决方案11】:

                将您的默认值插入到表变量中,然后使用您的实际表中的匹配项更新此 tableVar 的单行。如果找到一行,tableVar 将被更新;如果不是,则保留默认值。返回表变量。

                    ---=== The table & its data
                    CREATE TABLE dbo.Rates (
                        PkId int,
                        name varchar(10),
                        rate decimal(10,2)
                    )
                    INSERT INTO dbo.Rates(PkId, name, rate) VALUES (1, 'Schedule 1', 0.1)
                    INSERT INTO dbo.Rates(PkId, name, rate) VALUES (2, 'Schedule 2', 0.2)
                

                解决办法如下:

                ---=== The solution 
                CREATE PROCEDURE dbo.GetRate 
                  @PkId int
                AS
                BEGIN
                  DECLARE @tempTable TABLE (
                    PkId int, 
                    name varchar(10), 
                    rate decimal(10,2)
                 )
                
                 --- [1] Insert default values into @tempTable. PkId=0 is dummy value  
                 INSERT INTO @tempTable(PkId, name, rate) VALUES (0, 'DEFAULT', 0.00)
                
                 --- [2] Update the single row in @tempTable with the actual value.
                 ---     This only happens if a match is found
                 UPDATE @tempTable
                    SET t.PkId=x.PkId, t.name=x.name, t.rate = x.rate
                    FROM @tempTable t INNER JOIN dbo.Rates x
                    ON t.PkId = 0
                    WHERE x.PkId = @PkId
                
                 SELECT * FROM @tempTable
                END
                

                测试代码:

                EXEC dbo.GetRate @PkId=1     --- returns values for PkId=1
                EXEC dbo.GetRate @PkId=12314 --- returns default values
                

                【讨论】:

                  【解决方案12】:

                  这是我用来在没有值时获取默认值的方法。

                  select if ((select count(*) from `tbs`.`replication_status`) > 0, (select rs.`last_replication_end_date` from `tbs`.`replication_status` 
                            rs where rs.`last_replication_start_date` is not null and rs.`last_replication_end_date` 
                            is not null and rs.`table` = '%s' order by id desc limit 1), (select CAST(unix_timestamp(CURRENT_TIMESTAMP(6)) as UNSIGNED))) as ts;
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2020-01-30
                    • 2015-09-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-02-25
                    • 1970-01-01
                    • 2011-06-23
                    相关资源
                    最近更新 更多