oracle Ansi SQL谜语

rvpgvaaj  于 2023-03-29  发布在  Oracle
关注(0)|答案(2)|浏览(139)

我有一个表(在Oracle 12中,但我想只使用ANSI SQL),定义和填充如下:

CREATE TABLE MYTABLE (GROOM VARCHAR2(50), BRIDE VARCHAR2(50), STATE VARCHAR2(50));

INSERT INTO MYTABLE (GROOM,BRIDE,STATE) VALUES ('ALVIN','CARMEN','NJ');
INSERT INTO MYTABLE (GROOM,BRIDE,STATE) VALUES ('ALVIN','CARMEN','VA');
INSERT INTO MYTABLE (GROOM,BRIDE,STATE) VALUES ('ALVIN','ELEANOR','NJ');
INSERT INTO MYTABLE (GROOM,BRIDE,STATE) VALUES ('CARL','CARMEN','AL');
INSERT INTO MYTABLE (GROOM,BRIDE,STATE) VALUES ('CARL','ELEANOR','AL');
INSERT INTO MYTABLE (GROOM,BRIDE,STATE) VALUES ('DAVID','DIANA','NE');
INSERT INTO MYTABLE (GROOM,BRIDE,STATE) VALUES ('FRANK','DIANA','NV');
INSERT INTO MYTABLE (GROOM,BRIDE,STATE) VALUES ('MIKE',NULL,'RI');
INSERT INTO MYTABLE (GROOM,BRIDE,STATE) VALUES ('MIKE',NULL,'WI');

我想用这个标准得到一个结果:

  • 对于每个新郎(从字母顺序最低的名字),取具有字母顺序最低的名字和字母顺序最低的州(如果新娘是相同的)的“尚未结婚”的新娘。如果没有新娘可用,则将她设置为NULL。

例如:最低的新郎是Alvin,可以嫁给Carmen(新泽西州和弗吉尼亚州)或Eleanor。结果是:

Alvin, Carmen, NJ

现在最低的是卡尔,可以和卡门(但她已经和阿尔文结婚了)或者埃莉诺结婚,所以结果是:

Carl, Eleanor, AL

所以最后我想得到这个结果集:

Alvin, Carmen, NJ  
Carl, Eleanor, AL  
David, Diana, NE  
Frank, NULL, NV  
Mike, NULL, RI

正如我所说的,我只想使用ANSI SQL(所以我使用Oracle的事实是不相关的),没有临时表,游标或表自连接。窗口函数是可以的。
谢谢

balp4ylt

balp4ylt1#

嗯,首先,如果你能解释一下这些限制的性质,那就太好了。
例如,当有人想用纯SQL实现逻辑时,禁止自连接可能是合理的,但禁止自连接有什么意义呢?还有,你是否认为来自同一个表的相关子查询是自连接?标量子查询呢?
看起来你想用分析(也称为窗口)函数做一些技巧,但这是不可能的,因为在这个特定的情况下,因为你需要跟踪到目前为止已经保留了哪些新娘,而分析函数没有任何状态。
在Oracle中,有两种典型的方法可以处理类似于您的任务(“迭代”行并维护某些“状态”)

  • 递归子查询分解(也称为递归CTE)
  • 示范条款

让我从模型开始,尽管它是非常特定的Oracle特性

SQL> with t as
  2  (
  3    select *
  4    from mytable
  5    model
  6      dimension by (groom, bride, state)
  7      measures (0 reserved)
  8      (
  9        reserved[any,any,any] order by groom, bride, state
 10        = case
 11            -- current groom already has a bride
 12            when max(reserved)[cv(groom), lnnvl(bride > cv(bride)), any] = 1
 13            -- current bride already reserved for some groom
 14            or max(reserved)[groom < cv(groom), cv(bride), any] = 1
 15            then 0 else 1
 16          end
 17      )
 18  )
 19  select groom, bride, state
 20    from t
 21   where reserved = 1
 22   union all
 23  select groom, null, min(state)
 24    from mytable
 25   where groom not in (select groom from t where reserved = 1)
 26   group by groom
 27   order by 1;

GROOM      BRIDE      STATE
---------- ---------- ----------
ALVIN      CARMEN     NJ
CARL       ELEANOR    AL
DAVID      DIANA      NE
FRANK                 NV
MIKE                  RI

在此解决方案中,reserved列用于标记bride“已分配”的每一行。此方法仅适用于最初引入model子句的Oracle版本10g Release 1。
第二个解决方案如下

SQL> with rec(groom, bride, state, reserved)
  2       as (select min(groom),
  3                  min(bride) keep (dense_rank first order by groom),
  4                  min(state) keep (dense_rank first order by groom, bride),
  5                  min(bride) keep (dense_rank first order by groom)
  6             from mytable
  7           union all
  8           select t.groom,
  9                  t.bride,
 10                  t.state,
 11                  r.reserved || '#' || t.bride
 12             from rec r
 13             cross apply
 14              (select min(groom) groom,
 15                      min(bride) keep (dense_rank first order by groom) bride,
 16                      min(state) keep (dense_rank first order by groom, bride) state
 17                 from mytable
 18                where groom > r.groom and instr(r.reserved, bride) = 0) t
 19            where t.groom is not null)
 20             cycle groom set c to 1 default 0
 21  select groom, bride, state
 22    from rec
 23   union all
 24  select groom, null, min(state)
 25    from mytable
 26   where groom not in (select groom from rec)
 27   group by groom
 28   order by 1;

GROOM      BRIDE      STATE
---------- ---------- ----------
ALVIN      CARMEN     NJ
CARL       ELEANOR    AL
DAVID      DIANA      NE
FRANK                 NV
MIKE                  RI

在这个解决方案中,您可以摆脱特定的Oracle功能keep dense_rank,并避免使用仅在12c中引入的cross apply。此外,您可以使用集合而不是连接字符串来跟踪保留的新娘,但...再次,这是Oracle特定的解决方案。
然而,这个(经过微小的修改)可以被采用到,比如说SQL Server。
PS
说到性能,递归解决方案在每次执行递归成员时都会扫描整个mytable,这使得它在任何相对较大的数据集上都不可行。
model可能在数千行上很好,但它仍然为每行计算聚合(max(reserved)),这在非SQL方法中可以避免。

z9smfwbn

z9smfwbn2#

我对下面的查询做了一些测试。我不确定,所以请不要把它当作一个建议的解决方案(如果你发现一些概念上或事实上的错误,那将是非常感激的)。
这个想法是识别新郎和新娘的有序列表(通过窗口函数),然后如果新娘的排名上级配对的新郎,则更改新娘的名字,以便随后排除她的新郎分组并使用min运算符。

SELECT GROOM
/*oracle specific string functions, every system has its own equivalent*/
, REPLACE(SUBSTR(COMPOUND, 1, INSTR(COMPOUND, ';', 1, 1) -1), 'ZZZZZZZZZZ', NULL) AS BRIDE 
, SUBSTR(COMPOUND, INSTR(COMPOUND, ';', -1, 1) +1) AS STATE
FROM
(
    SELECT GROOM
    , MIN(CASE WHEN RANK_GROOM < RANK_BRIDE AND RANK_BRIDE <> 1 THEN 'ZZZZZZZZZZ' 
ELSE BRIDE END || ';' || STATE) AS COMPOUND
    FROM
    (
        SELECT
        GROOM, COALESCE(BRIDE, 'ZZZZZZZZZZ') AS BRIDE, STATE
        , DENSE_RANK() OVER (PARTITION BY GROOM ORDER BY BRIDE, STATE) AS RANK_GROOM
        , DENSE_RANK() OVER (PARTITION BY BRIDE ORDER BY GROOM, STATE) AS RANK_BRIDE
        FROM MYTABLE
    ) T1 
    GROUP BY GROOM
) T2

p.s.使用'ZZZZZZZZ'这个名字显然不是'优雅',但在我的真实的情况中,我正在处理数字,所以它可以被认为是最大数值常量的替代品。
编辑:我在这些天做了很多测试,似乎上面的查询符合我的所有需求。

相关问题