【问题标题】:Oracle union giving errorOracle union 给出错误
【发布时间】:2023-03-28 23:49:01
【问题描述】:

我正在尝试在 Oracle 中创建一个视图,以显示酒店中所有未预订的可用房间。

如果他们被预订了,RoomBasket 中就会有记录。所以我试图在入住和退房日期之间选择所有不在 RoomBasket 中的房间(RoomBasket.datein、RoomBasket.dateout)

但是当我使用内部联接和外部(左/右)联接时,查询一直返回 0 个结果,我认为这是因为它没有加入日期表,所以它会有 0 个结果。所以现在我正在尝试对记录所有房间的 Room 表进行联合,因为我认为这会选择所有房间,然后否定已预订的房间?

我的语法不正确,我玩弄了这个查询的大量形式:

CREATE VIEW availability AS
    (SELECT * FROM RoomBasket rb
        WHERE TO_DATE(SYSDATE, 'yyyymmdd') 
        NOT BETWEEN TO_DATE(rb.datein, 'yyyymmdd') AND TO_DATE(rb.dateout, 'yyyymmdd')
        UNION (SELECT r.id room, rt.type type, rt.price price FROM Room r, RoomType rt)
    );

但如果它有效,我会得到 0 个结果,如果它不起作用,我会得到语法错误。目前的错误是:

查询块的结果列数不正确

【问题讨论】:

  • 不要在第一个查询中使用SELECT *,实际列出你需要的列。 UNION 要求所有查询中的列数相同。
  • @bluefeet 好的,谢谢!

标签: sql database oracle join union


【解决方案1】:

RoomRoomType 的连接没有谓词,因此您获得了交叉连接。这不太可能是你想要的。

此外,您的union 似乎正在尝试将不相关的数据添加到房间数据中。您对问题的描述表明您希望使用 RoomBasket 数据来过滤其他数据——这需要连接或子查询。

更多类似的东西会做你想做的事:

CREATE VIEW availability AS (
  SELECT r.id room, rt.type type, rt.price price
  FROM
    Room r
    INNER JOIN RoomType rt
      ON rt.id = r.type
    LEFT JOIN RoomBasket rb
      ON rb.room = r.id
        AND TO_DATE(SYSDATE, 'yyyymmdd') BETWEEN TO_DATE(rb.datein, 'yyyymmdd')
          AND TO_DATE(rb.dateout, 'yyyymmdd')
  WHERE rb.room IS NULL
);

WHERE 谓词的作用是选择左表 (Room JOIN RoomType) 中与右表 (RoomBasket) 的任何行都不匹配的那些行。

【讨论】:

    【解决方案2】:

    您的第一个查询返回的列数与您的第二个查询不同,因此它们无法合并。明确指定第一个查询的列,而不是使用SELECT *

    【讨论】:

      【解决方案3】:

      我让它像这样正常工作:

      CREATE VIEW availability AS
                  (SELECT 
                      r.id room, 
                      rt.type type, 
                      rt.price price 
                      FROM Room r 
                      INNER JOIN RoomType rt ON r.type = rt.id
                      INNER JOIN RoomBasket rb ON r.id = rb.room
                      WHERE TO_DATE(SYSDATE, 'DD-Mon-YYYY') 
                          NOT BETWEEN TO_DATE(rb.datein, 'DD-Mon-YYYY') 
                                  AND TO_DATE(rb.dateout, 'DD-Mon-YYYY')
                  )
          ;
      

      【讨论】:

      • 看起来这意味着返回在您使用的日期范围之外有多个对应的RoomBasket 行的房间的副本。不过,有足够的信息让我用正确的列名更新我的答案。
      • @JohnBollinger 是的,一个给出了不正确的结果,但我已经像这样工作了。编辑了答案。
      • 不,那更糟。它不仅仍然可以产生欺骗,它还会错过个没有对应RoomBasket行的房间。也许您的RoomBasket 数据恰好会产生您期望的结果,但查询的形式并不能确保它会继续这样做。
      • SYSDATE 是一个日期;你不需要打电话给TO_DATE。当您这样做时,它会隐式转换为字符串,然后显式转换回日期。充其量这只是不必要的工作;但如果隐式转换格式与显式转换格式不同,则会导致错误的结果。
      猜你喜欢
      • 1970-01-01
      • 2014-12-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-01
      相关资源
      最近更新 更多