【问题标题】:Return values from different tables based on a priority根据优先级从不同的表返回值
【发布时间】:2010-09-16 04:13:12
【问题描述】:

假设我有三个表: 表A(键,值) 表B(键,值) TableC(键、值)

我想为所有键返回一个值。如果键存在于 TableC 中,则返回该值,否则如果键存在于 B 中,则返回该值,否则返回表 A 中的值

到目前为止我想出的最好的是

SELECT key,Value
FROM TableA
WHERE key NOT IN (SELECT key FROM TableB)
    AND key NOT IN (SELECT key FROM TableC)
UNION
SELECT key,Value
FROM TableB
WHERE key NOT IN (SELECT key FROM TableC)
UNION
SELECT key,Value
FROM TableC

但这似乎很暴力。有人知道更好的方法吗?

编辑:这是一个更具体的例子。将 TableA 视为标准工作计划,其中键是日期,值是分配的班次。表 B 是覆盖标准工作周的法定假日日历。表 C 是一个例外时间表,用于在有人被要求加入额外班次或不同班次时覆盖其他两个时间表。

【问题讨论】:

    标签: sql


    【解决方案1】:

    如果您有许多表要从中获取数据,那么您将不得不从它们中进行选择,没有其他方法可以解决。

    从您的 SQL 中,您似乎可以从 tableC 中获得包含 tableA 和 tableB 中的键的结果,因为您正在对 tableC 上的简单选择的结果进行联合运算(其中没有 where 子句)。在任何其他表中都不存在的一组专有键之后,您在哪里?如果是这样,那么您将需要在 tableB 和 tableC 的选择中为 tableA 的 where 子句执行您所做的操作。

    我希望这是有道理的......

    【讨论】:

    • 等等...他希望 C 的值覆盖 A 和 B 的值。
    • @David B. 是的,请参阅我添加的具体示例,也许这会澄清我的不清楚。
    【解决方案2】:

    您的查询看起来不错。

    或者,您可以使用下面的查询并在客户端进行过滤。数据库服务器的压力会更小。

    SELECT key, value, 2 AS priority
    FROM TableA
    UNION
    SELECT key, value, 1 AS priority
    FROM TableB
    UNION
    SELECT key, value, 0 AS priority
    FROM TableC
    ORDER BY key, priority
    

    【讨论】:

      【解决方案3】:

      以下是我在 SQL Server 中的操作方式。此解决方案应生成比原始解决方案更少的逻辑 IO。如果表足够大,我会切换到 #temp 表以启用并行性。

      DECLARE @MyTable TABLE
      (
        Key int PRIMARY KEY,
        Value int
      )
      
          --Grab from TableC
      INSERT INTO @MyTable(Key, Value)
      SELECT Key, Value
      FROM TableC
      
          --Grab from TableB
      INSERT INTO @MyTable(Key, Value)
      SELECT Key, Value
      FROM TableB
      WHERE Key not in (SELECT Key FROM @MyTable)
      
          --Grab from TableA  
      INSERT INTO @MyTable(Key, Value)
      SELECT Key, Value
      FROM TableA
      WHERE Key not in (SELECT Key FROM @MyTable)
          --Pop the result
      SELECT Key, Value
      FROM @MyTable
      

      这项技术反映了我如何在 C# 中处理 3 个列表...通过创建字典。

      【讨论】:

      • 在问题中将它作为单个 SQL 语句运行不是更好吗?这将减少与数据库的往返次数,并为优化器提供更好的聪明机会。
      • 这是一个脚本,所以没有往返。优化器对 UNION 不智能,无法识别在查询中使用 else-where 的表。是的,我正在做优化器的工作。
      【解决方案4】:

      这是一个替代的 SQL 语句:-

      SELECT
          ALL_KEYS.KEY,
          NVL( TABLEC.VALUE, NVL( TABLEB.VALUE, TABLEA.VALUE)) AS VALUE
      FROM
          (SELECT KEY AS KEY FROM TABLEA
           UNION
           SELECT KEY FROM TABLEB
           UNION
           SELECT KEY FROM TABLEC) ALL_KEYS,
           TABLEA,
           TABLEB,
           TABLEC
      WHERE
          ALL_KEYS.KEY = TABLEA.KEY(+) AND
          ALL_KEYS.KEY = TABLEB.KEY(+) AND
          ALL_KEYS.KEY = TABLEC.KEY(+);
      

      注意。 NVL() 是一个 Oracle 函数。如果第一个参数为NULL,则返回第二个参数,否则返回第一个参数。你没有说你使用的是哪个数据库,但毫无疑问,所有东西都有等价物。

      【讨论】:

        【解决方案5】:

        好的,以您的具体示例为基础,我想出了一个与其他发布的解决方案不同的解决方案(尽管我认为我更喜欢您的解决方案)。这是在 MS SQL Server 2005 上测试的 - 您的 SQL 方言可能需要更改。

        首先,一些 DDL 做准备:

        CREATE TABLE [dbo].[StandardSchedule](
            [scheduledate] [datetime] NOT NULL,
            [shift] [varchar](25) NOT NULL,
         CONSTRAINT [PK_StandardSchedule] PRIMARY KEY CLUSTERED 
        ( [scheduledate] ASC ));
        
        CREATE TABLE [dbo].[HolidaySchedule](
            [holidaydate] [datetime] NOT NULL,
            [shift] [varchar](25) NOT NULL,
         CONSTRAINT [PK_HolidaySchedule] PRIMARY KEY CLUSTERED 
        ( [holidaydate] ASC ));
        
        CREATE TABLE [dbo].[ExceptionSchedule](
            [exceptiondate] [datetime] NOT NULL,
            [shift] [varchar](25) NOT NULL,
         CONSTRAINT [PK_ExceptionDate] PRIMARY KEY CLUSTERED 
        ( [exceptiondate] ASC ));
        
        INSERT INTO ExceptionSchedule VALUES ('2008.01.06', 'ExceptionShift1');
        INSERT INTO ExceptionSchedule VALUES ('2008.01.08', 'ExceptionShift2');
        INSERT INTO ExceptionSchedule VALUES ('2008.01.10', 'ExceptionShift3');
        INSERT INTO HolidaySchedule VALUES ('2008.01.01', 'HolidayShift1');
        INSERT INTO HolidaySchedule VALUES ('2008.01.06', 'HolidayShift2');
        INSERT INTO HolidaySchedule VALUES ('2008.01.09', 'HolidayShift3');
        INSERT INTO StandardSchedule VALUES ('2008.01.01', 'RegularShift1');
        INSERT INTO StandardSchedule VALUES ('2008.01.02', 'RegularShift2');
        INSERT INTO StandardSchedule VALUES ('2008.01.03', 'RegularShift3');
        INSERT INTO StandardSchedule VALUES ('2008.01.04', 'RegularShift4');
        INSERT INTO StandardSchedule VALUES ('2008.01.05', 'RegularShift5');
        INSERT INTO StandardSchedule VALUES ('2008.01.07', 'RegularShift6');
        INSERT INTO StandardSchedule VALUES ('2008.01.09', 'RegularShift7');
        INSERT INTO StandardSchedule VALUES ('2008.01.10', 'RegularShift8');
        

        使用这些表/行作为基础,此 SELECT 语句检索所需的数据:

        SELECT DISTINCT
            COALESCE(e2.exceptiondate, e.exceptiondate, holidaydate, scheduledate) AS ShiftDate,
            COALESCE(e2.shift, e.shift, h.shift, s.shift) AS Shift
        FROM standardschedule s
        FULL OUTER JOIN holidayschedule h ON s.scheduledate = h.holidaydate
        FULL OUTER JOIN exceptionschedule e ON h.holidaydate = e.exceptiondate
        FULL OUTER JOIN exceptionschedule e2 ON s.scheduledate = e2.exceptiondate
        ORDER BY shiftdate
        

        【讨论】:

        • 完全加入星形,很好。 (+1)
        • 非常优雅的解决方案!似乎也有很好的表现
        【解决方案6】:
        SELECT isnull( c.key, isnull( b.key, a.key) ) , 
               isnull( c.value, isnull( b.value, a.value ) ) 
        FROM   TableA a 
        LEFT JOIN TableB b 
        ON        a.key = b.key
        LEFT JOIN TableC c 
        ON        b.key = c.key
        

        【讨论】:

          【解决方案7】:

          创建一个包含所有键的主表,然后将此主表左连接到三个表并研究COALESCE 命令。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-05-04
            • 1970-01-01
            • 1970-01-01
            • 2020-05-11
            • 2021-11-10
            相关资源
            最近更新 更多