获取 ID 和最大值 ORACLE SQL

Get ID alongside max value ORACLE SQL

我目前有:

TABLE "QUARTO":

CREATE TABLE Quarto (
  Id                        number(2) NOT NULL, 
  LotacaoMaxima             number(1) NOT NULL, 
  TipoQuartoId              number(1) NOT NULL, 
  NumeroQuartoNumSequencial number(3) NOT NULL, 
  NumeroQuartoAndarId       varchar2(1) NOT NULL, 
  PRIMARY KEY (Id));

TABLE RESERVA:

CREATE TABLE Reserva (
  Id               number(3) NOT NULL, 
  ClienteNif       number(9) NOT NULL, 
  QuartoId         number(2) NOT NULL, 
  DataInicio       date NOT NULL, 
  DataFim          date NOT NULL, 
  NumPessoas       number(1) NOT NULL, 
  Estado           varchar2(15) NOT NULL, 
  DataCancelamento date, 
  PRIMARY KEY (Id));

我在两者中都有的一些数据是:

QUARTO:

| ID | LOTACAOMAXIMA | TIPOQUARTOID | NUMEROQUARTONUMSEQUENCIAL | NUMEROQUARTOANDARID |
| :--- | :--- | :--- | :--- | :--- |
| 1 | 1 | 1 | 1 | 1 |
| 2 | 1 | 1 | 2 | 1 |
| 3 | 1 | 1 | 3 | 1 |
| 4 | 1 | 1 | 4 | 1 |
| 5 | 1 | 1 | 5 | 1 |
| 6 | 1 | 1 | 6 | 1 |
| 7 | 1 | 1 | 7 | 1 |
| 8 | 1 | 1 | 8 | 1 |
| 9 | 1 | 1 | 9 | 1 |
| 10 | 1 | 1 | 10 | 1 |
| 11 | 2 | 2 | 11 | 1 |
| 12 | 2 | 2 | 12 | 1 |
| 13 | 2 | 2 | 13 | 1 |
| 14 | 2 | 2 | 14 | 1 |
| 15 | 2 | 2 | 15 | 1 |
| 16 | 2 | 2 | 16 | 1 |
| 17 | 2 | 2 | 17 | 1 |
| 18 | 2 | 2 | 18 | 1 |
| 19 | 2 | 2 | 19 | 1 |
| 20 | 2 | 2 | 20 | 1 |


RESERVA:

| ID | CLIENTENIF | QUARTOID | DATAINICIO | DATAFIM | NUMPESSOAS | ESTADO | DATACANCELAMENTO |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| 1 | 296837970 | 11 | 2020-06-01 00:00:00 | 2020-06-12 00:00:00 | 1 | Finalizada | NULL |
| 2 | 275784703 | 17 | 2020-06-13 00:00:00 | 2020-06-21 00:00:00 | 1 | Finalizada | NULL |
| 3 | 220347654 | 11 | 2020-07-07 00:00:00 | 2020-07-15 00:00:00 | 2 | Finalizada | NULL |
| 4 | 294772545 | 12 | 2020-08-01 00:00:00 | 2020-08-15 00:00:00 | 2 | Finalizada | NULL |
| 5 | 220347654 | 3 | 2020-01-01 00:00:00 | 2020-01-16 00:00:00 | 1 | Finalizada | NULL |
WITH CONTAGEM_QUARTO_POR_ID AS (SELECT q.ID, COUNT(r.QUARTOID) AS NUM_RESERVAS, q.TIPOQUARTOID, q.NUMEROQUARTOANDARID
                                FROM RESERVA r
                                         INNER JOIN QUARTO q on q.ID = r.QUARTOID
                                GROUP BY q.ID, q.TIPOQUARTOID, q.NUMEROQUARTOANDARID)
SELECT t.TIPOQUARTOID, t.NUMEROQUARTOANDARID, MAX(t.NUM_RESERVAS) AS MAX
FROM CONTAGEM_QUARTO_POR_ID t
GROUP BY t.TIPOQUARTOID, t.NUMEROQUARTOANDARID

输出如下:

| TIPOQUARTOID | NUMEROQUARTOANDARID | MAX |
| :----------- | :------------------ | :-- |
| 1            |                   2 |   2 |
| 2            |                   1 |   8 |
| 1            |                   1 |   1 |

除了我目前拥有的数据,我还想显示每一行的脚趾 ID,但是当我将 t.ID 添加到 SELECT 时,它迫使我将其添加到 GROUP BY 输出是这样的:

| TIPOQUARTOID | NUMEROQUARTOANDARID | MAX | ID |
| :----------- | :------------------ | :-- | :- |
| 2            | 1                   | 2   | 11 |
| 1            | 1                   | 1   | 1  |
| 1            | 1                   | 1   | 3  |
| 1            | 2                   | 2   | 21 |
| 2            | 1                   | 1   | 17 |
| 2            | 1                   | 1   | 12 |
| 2            | 1                   | 8   | 16 |

我只想获取最大值和与该 MAX 关联的 ID。

您需要 MAX() OVER () 具有 PARTITION BY TIPOQUARTOID, NUMEROQUARTOANDARIDNUM_RESERVAS 列的分析函数,以便按列表对分区内的那些列进行分组,例如

WITH CONTAGEM_QUARTO_POR_ID AS
(
  SELECT q.ID,
         COUNT(r.QUARTOID) AS NUM_RESERVAS,
         q.TIPOQUARTOID,
         q.NUMEROQUARTOANDARID
    FROM RESERVA r
    JOIN QUARTO q
      on q.ID = r.QUARTOID
   GROUP BY q.ID, q.TIPOQUARTOID, q.NUMEROQUARTOANDARID
), t AS
(
 SELECT t.TIPOQUARTOID, t.NUMEROQUARTOANDARID, t.NUM_RESERVAS, 
        MAX(t.NUM_RESERVAS) 
        OVER (PARTITION BY t.TIPOQUARTOID, t.NUMEROQUARTOANDARID) AS MAX, 
        t.ID
   FROM CONTAGEM_QUARTO_POR_ID t
)
SELECT TIPOQUARTOID, NUMEROQUARTOANDARID, NUM_RESERVAS, ID
  FROM t
 WHERE NUM_RESERVAS = MAX

或使用 HAVING 子句更直接

SELECT q.TIPOQUARTOID,
       q.NUMEROQUARTOANDARID,
       COUNT(r.QUARTOID) AS NUM_RESERVAS,
       q.ID
  FROM RESERVA r
  JOIN QUARTO q
    on q.ID = r.QUARTOID
 GROUP BY q.ID, q.TIPOQUARTOID, q.NUMEROQUARTOANDARID
 HAVING COUNT(r.QUARTOID) = q.TIPOQUARTOID

您可以在您的查询中使用 KEEP 子句,而无需像下面那样做太多更改:

WITH CONTAGEM_QUARTO_POR_ID AS 
  (SELECT q.ID, COUNT(r.QUARTOID) AS NUM_RESERVAS, q.TIPOQUARTOID, q.NUMEROQUARTOANDARID
     FROM RESERVA r
     INNER JOIN QUARTO q on q.ID = r.QUARTOID
   GROUP BY q.ID, q.TIPOQUARTOID, q.NUMEROQUARTOANDARID)
SELECT t.TIPOQUARTOID, t.NUMEROQUARTOANDARID, MAX(t.NUM_RESERVAS) AS MAX, 
       max(t.ID) keep(dense_rank first order by t.NUM_RESERVAS desc nulls last) as ID -- this
FROM CONTAGEM_QUARTO_POR_ID t
GROUP BY t.TIPOQUARTOID, t.NUMEROQUARTOANDARID

我不建议采用两级聚合。只需使用 window 函数:

WITH CONTAGEM_QUARTO_POR_ID AS (
      SELECT q.ID, COUNT(*) AS NUM_RESERVAS, q.TIPOQUARTOID, q.NUMEROQUARTOANDARID,
             ROW_NUMBER() OVER (PARTITION BY q.TIPOQUARTOID, q.NUMEROQUARTOANDARID ORDER BY COUNT(*) DESC) as seqnum
      FROM RESERVA r INNER JOIN
           QUARTO q 
           ON q.ID = r.QUARTOID
      GROUP BY q.ID, q.TIPOQUARTOID, q.NUMEROQUARTOANDARID
     )
SELECT cq.*
FROM CONTAGEM_QUARTO_POR_ID cq
WHERE seqnum = 1;

我认为这会比两个聚合的性能略好(但值得检查)。

这种方法的一个优点是它更灵活。如果你想要领带,只需在子查询中将 ROW_NUMBER() 更改为 RANK()

也许更重要的是,ROW_NUMBER() 是 SQL 中的一个“成语”,用于每组返回一行(或特定行数)。学习如何使用它非常有价值。