我有一个这样定义和填充的表(在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');
我希望获得符合以下条件的结果:
例如:最低的新郎是阿尔文,可以与卡门(在新泽西州和弗吉尼亚州)或埃莉诺结婚。结果是:
Alvin, Carmen, NJ
现在最低的是卡尔,可以嫁给卡门(但她已经与阿尔文结婚)或埃莉诺。所以结果是:
Carl, Eleanor, AL
所以最后我想得到这个结果集:
Alvin, Carmen, NJ
Carl, Eleanor, AL
David, Diana, NE
Frank, NULL, NV
Mike, NULL, RI
正如我所说,我只想使用ANSI SQL(因此,我使用Oracle是不相关的),没有临时表,游标或表自联接。 窗口功能还可以。
谢谢
答案 0 :(得分:1)
好吧,首先,如果您可以解释这些限制的性质,那就太好了。
例如,当某人想要在纯SQL中实现逻辑时可能是合理的,但是禁止自我联接的意义何在?此外,您是否将同一表中的相关子查询视为自我联接?标量子查询呢?
看起来您想对解析函数(也称为窗口)进行一些技巧,但这是不可能的,因为在此特定情况下,因为您需要跟踪到目前为止已保留的新娘,并且解析函数没有任何种类状态。
在Oracle中,有两种典型的方法可用于处理您的任务(当您“迭代”行并维护一些“状态”时)
让我从模型开始,即使它是非常具体的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
用于标记“分配”新娘的每一行。最初引入了模型子句后,该方法仅在10g 1版开始的Oracle中有效。
第二个解决方案是
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
。另外,您可以使用collection而不是串联字符串来跟踪保留的新娘,但是...这又是Oracle特定的解决方案。
但是,这个(稍作修改)可以用于SQL Server。
PS。
谈到性能,递归解决方案在每次执行递归成员时都会扫描整个mytable,这使得它在任何较大的数据集上都不可行。
model
可能适用于数千行,但仍会为每行计算聚合(max(reserved)
),这在非SQL方法中可以避免。
答案 1 :(得分:0)
我正在使用以下查询进行一些测试。 我不确定,所以请不要将其视为建议的解决方案(如果您发现一些概念性或事实上的错误,将不胜感激)。
此想法是(通过窗口功能)确定新郎和新娘的有序列表,然后在新娘的等级高于已婚新郎的情况下更改新娘的名称,以随后排除新郎和新娘的分组。使用最小运算符。
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。名称“ ZZZZZZZZZZZZ”的使用显然不是“优雅”的,但在我的实际情况下,我正在处理数字,因此可以将其视为最大数值常数的替代。
编辑:这几天我做了很多测试,上面的查询似乎可以满足我的所有需求。