繁体   English   中英

如何为不返回任何行的查询设置默认行?

[英]How to set a default row for a query that returns no rows?

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

编辑:这将是 SQL Server。

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的事实。

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

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。)

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

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'

这个怎么样:

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)

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

语法适用于 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 );

我想通了,它也应该适用于其他系统。 这是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 )

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

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

*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 的评论。

假设在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中不存在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

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

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

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

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

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

将您的默认值插入到表变量中,然后使用实际表中的匹配项更新此 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

如果不存在任何值,这就是我用于获取默认值的方法。

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;

我会用图片来回答..

us1ic.png

  • 代码

     declare @someTable table ( Pk int primary key , Column1 nvarchar(max) , Column2 nvarchar(max) not null , Column3 nvarchar(max) ) ; with model as ( select y.* from ( select null whatever ) x left join @someTable y on (y.Pk=x.whatever) ) select * from model where ( not exists ( select null from @someTable ) ) union all select * from @someTable

cte model从表模式生成一行,所有列都设置为null

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM