【问题标题】:Oracle SQL only: Case statement or exists query to show results based on condition仅限 Oracle SQL:Case 语句或存在查询以根据条件显示结果
【发布时间】:2021-05-13 18:14:13
【问题描述】:

我正在尝试使用 case 语句根据某些条件创建计算列。我离目标很近,但看不到查询出错的地方。希望我能在这里得到一些最好/更简单的方法和一些帮助。

以下是表格:
PERSON 表格:

+----+--------+
| ID | PERSON |
+----+--------+
|  1 | John   |
|  2 | Scott  |
|  3 | Ruth   |
|  4 | Smith  |
|  5 | Frank  |
|  6 | Martin |
|  7 | Blake  |
+----+--------+

角色表:

+----+------+
| ID | ROLE |
+----+------+
|  1 | JJJ  |
|  2 | Auth |
|  3 | AAA  |
|  4 | MMM  |
|  5 | KKK  |
|  6 | BBB  |
+----+------+

最后一个是明细表
PERSON_ROLE 表:

+----+-----------+---------+
| ID | PERSON_ID | ROLE_ID |
+----+-----------+---------+
|  1 |         1 |       1 |
|  2 |         2 |       2 |
|  3 |         2 |       3 |
|  4 |         2 |       4 |
|  5 |         3 |       1 |
|  6 |         3 |       5 |
|  7 |         4 |       3 |
|  8 |         5 |       6 |
|  9 |         6 |       3 |
| 10 |         6 |       6 |
| 11 |         6 |       2 |
| 12 |         7 |       5 |
| 13 |         7 |       6 |
+----+-----------+---------+

期望/预期输出:

+--------+--------+----------+
| PERSON | MYROLE | MYACCESS |
+--------+--------+----------+
| John   | JJJ    |          |
| Scott  | Auth   | Remove   |
| Scott  | AAA    |          |
| Scott  | MMM    |          |
| Ruth   | JJJ    |          |
| Ruth   | KKK    |          |
| Smith  | AAA    | Add      |
| Frank  | BBB    | Add      |
| Martin | AAA    |          |
| Martin | BBB    |          |
| Martin | Auth   | Remove   |
| Blake  | KKK    |          |
| Blake  | BBB    | Add      |
+--------+--------+----------+

条件如下:

  1. 如果 Person 具有 Role “AAA”或“BBB”而不是“Auth”,则 MYAccess 列应将值显示为“Add”为那个人。所有其他值都应为 null。
  2. 如果 Person 具有 Role “Auth”,则 MYAccess 列应仅针对该行显示“Remove”。即使同一个人有“AAA”或“BBB”或任何其他值,它也应该显示为 null。

以下是我得到的部分正确的实际输出:

+--------+--------+----------+
| PERSON | MYROLE | MYACCESS |
+--------+--------+----------+
| Blake  | KKK    |          |
| Blake  | BBB    | Add      |
| Frank  | BBB    | Add      |
| John   | JJJ    |          |
| Martin | AUTH   | Remove   |
| Martin | BBB    | Add      |
| Martin | AAA    | Add      |
| Ruth   | JJJ    |          |
| Ruth   | KKK    |          |
| Scott  | AAA    | Add      |
| Scott  | AUTH   | Remove   |
| Scott  | MMM    |          |
| Smith  | AAA    | Add      |
+--------+--------+----------+

对于人 Martin 和 Scott,它应该只显示“删除”,但我也得到了“添加”。

下面是查询:

SELECT p.person,upper(r.role) myrole,
case when p.person in (select p.person from  person ut where ut.person = p.person and upper(r.role) = upper('Auth')) then 'Remove' 
     when p.person in (select p.person from  person ut where ut.person = p.person and (upper(r.role) = upper('Auth') or (upper(r.role) = upper('AAA') or upper(r.role) = upper('BBB')))) then 'Add' else null
end as myaccess
FROM person p
       join person_role pr
         ON p.id = pr.person_id
       join myrole r
         ON r.id = pr.role_id
order by p.person

DDL 脚本:

CREATE TABLE person (
    id      INTEGER NOT NULL,
    person  VARCHAR2(50 CHAR)
);

INSERT INTO person (
    id,
    person
) VALUES (
    1,
    'John'
);

INSERT INTO person (
    id,
    person
) VALUES (
    2,
    'Scott'
);

INSERT INTO person (
    id,
    person
) VALUES (
    3,
    'Ruth'
);

INSERT INTO person (
    id,
    person
) VALUES (
    4,
    'Smith'
);

INSERT INTO person (
    id,
    person
) VALUES (
    5,
    'Frank'
);

INSERT INTO person (
    id,
    person
) VALUES (
    6,
    'Martin'
);

INSERT INTO person (
    id,
    person
) VALUES (
    7,
    'Blake'
);

ALTER TABLE person ADD CONSTRAINT person_pk PRIMARY KEY ( id );


CREATE TABLE myrole (
    id    INTEGER NOT NULL,
    role  VARCHAR2(50 CHAR)
);

INSERT INTO myrole (
    id,
    role
) VALUES (
    1,
    'JJJ'
);

INSERT INTO myrole (
    id,
    role
) VALUES (
    2,
    'Auth'
);

INSERT INTO myrole (
    id,
    role
) VALUES (
    3,
    'AAA'
);

INSERT INTO myrole (
    id,
    role
) VALUES (
    4,
    'MMM'
);

INSERT INTO myrole (
    id,
    role
) VALUES (
    5,
    'KKK'
);

INSERT INTO myrole (
    id,
    role
) VALUES (
    6,
    'BBB'
);

ALTER TABLE myrole ADD CONSTRAINT myrole_pk PRIMARY KEY ( id );


CREATE TABLE person_role (
    id         INTEGER NOT NULL,
    person_id  INTEGER,
    myrole_id  INTEGER
);

ALTER TABLE person_role ADD CONSTRAINT person_role_pk PRIMARY KEY ( id );

CREATE SEQUENCE myrole_seq START WITH 1 NOCACHE;

CREATE OR REPLACE TRIGGER myrole_tr BEFORE
    INSERT ON myrole
    FOR EACH ROW
    WHEN ( new.id IS NULL )
BEGIN
    :new.id := myrole_seq.nextval;
END;
/

CREATE SEQUENCE person_seq START WITH 1 NOCACHE;

CREATE OR REPLACE TRIGGER person_tr BEFORE
    INSERT ON person
    FOR EACH ROW
    WHEN ( new.id IS NULL )
BEGIN
    :new.id := person_seq.nextval;
END;
/

CREATE SEQUENCE person_role_seq START WITH 1 NOCACHE;

CREATE OR REPLACE TRIGGER person_role_tr BEFORE
    INSERT ON person_role
    FOR EACH ROW
    WHEN ( new.id IS NULL )
BEGIN
    :new.id := person_role_seq.nextval;
END;
/

谢谢
里查

【问题讨论】:

  • 看起来是数据问题,或者您的 ADD 部分的 OR 语句需要一些工作。我在你的桌子上看到了 Scott 和 Martin 两次。第一个与 Remove 列匹配,并且您的 OR 语句逻辑看起来像是将这 2 条记录的第二行作为 ADD。尝试将您的案例陈述分成几个部分并单独运行以进行测试以缩小范围。
  • 另外,如果您可以发布可重复的代码,以便我们可以复制/粘贴它来运行,它将帮助您更快、更轻松地获得帮助stackoverflow.com/help/minimal-reproducible-example
  • "join" 不是一种表。您所谓的“连接”表称为“详细”表。我将编辑您的帖子以进行更改,我想让您知道我在做什么以及为什么。
  • 另外,您确定您的明细表是正确的吗?在查询中,它似乎只有 id(应该如此),而不是名称和角色名称。您的“详细信息”表 - 正如现在发布的那样 - 已经包含名称和角色名称,无需加入详细信息表。
  • @mathguy 谢谢你的改变。详细信息表只有 ID。我展示了所需的输出表作为示例,我的输出应该是怎样的,我编写的查询除了计算不正确

标签: sql oracle case-statement


【解决方案1】:

这就是你要找的吗?
在你的条件下,你说: “如果 Person 具有角色“AAA”或“BBB”而不是“Auth”,则 MYAccess 列应显示该 Person 的值为“Add”。所有其他值应为 null。 " Scott 有 Auth,所以这个条件产生错误。根据这条规则,斯科特不应该有“添加”。在您预期的输出中,它具有“添加”。我错过了什么吗?

WITH person_roles (id, person_id, role_id) AS
(
SELECT  1, 1,1 FROM DUAL UNION ALL
SELECT  2, 2,2 FROM DUAL UNION ALL
SELECT  3, 2,3 FROM DUAL UNION ALL
SELECT  4, 2,4 FROM DUAL UNION ALL
SELECT  5, 3,1 FROM DUAL UNION ALL
SELECT  6, 3,5 FROM DUAL UNION ALL
SELECT  7, 4,3 FROM DUAL UNION ALL
SELECT  8, 5,6 FROM DUAL UNION ALL
SELECT  9, 6,3 FROM DUAL UNION ALL
SELECT 10, 6,6 FROM DUAL UNION ALL
SELECT 11, 6,2 FROM DUAL UNION ALL
SELECT 12, 7,5 FROM DUAL UNION ALL
SELECT 13, 7,6 FROM DUAL
),persons (id, person) AS
(
SELECT   1,'John' FROM DUAL UNION ALL   
SELECT   2,'Scott' FROM DUAL UNION ALL  
SELECT   3,'Ruth' FROM DUAL UNION ALL   
SELECT   4,'Smith' FROM DUAL UNION ALL  
SELECT   5,'Frank' FROM DUAL UNION ALL  
SELECT   6,'Martin' FROM DUAL UNION ALL 
SELECT   7,'Blake' FROM DUAL
),roles (id, role) AS
(
SELECT 1,'JJJ' FROM DUAL UNION ALL   
SELECT 2,'Auth' FROM DUAL UNION ALL  
SELECT 3,'AAA' FROM DUAL UNION ALL   
SELECT 4,'MMM' FROM DUAL UNION ALL   
SELECT 5,'KKK' FROM DUAL UNION ALL   
SELECT 6,'BBB' FROM DUAL 
), rule_1(person_id, role_type)  AS
(
SELECT p.id, MIN(CASE WHEN r.ROLE = 'Auth' THEN 0 WHEN r.ROLE in ('AAA','BBB') THEN 1 ELSE 2 END) 
  FROM person_roles pr 
  JOIN persons p ON p.id = pr.person_id 
  JOIN roles r ON r.id = pr.role_id
  GROUP BY p.id
)
SELECT p.person, r.role, 
  CASE 
    WHEN rl.role_type = 1 AND r.role IN ('AAA','BBB') THEN 'Add'  
    WHEN rl.role_type = 0 AND r.role = 'Auth' THEN 'Remove' 
  END as action
  FROM person_roles pr 
  JOIN persons p ON p.id = pr.person_id 
  JOIN roles r ON r.id = pr.role_id
  JOIN rule_1 rl ON rl.person_id = pr.person_id
ORDER BY p.person

PERSON ROLE ACTION
------ ---- ------
Blake  BBB  Add   
Blake  KKK        
Frank  BBB  Add   
John   JJJ        
Martin AAA        
Martin Auth Remove
Martin BBB        
Ruth   KKK        
Ruth   JJJ        
Scott  MMM        
Scott  AAA        
Scott  Auth Remove
Smith  AAA  Add   

如果您想避免 CTE(with 子句),您可以将最后 2 个语句替换为 1:

SELECT p.person, r.role, 
  CASE 
    WHEN rl.role_type = 1 AND r.role IN ('AAA','BBB') THEN 'Add'  
    WHEN rl.role_type = 0 AND r.role = 'Auth' THEN 'Remove' 
  END as action
  FROM person_roles pr 
  JOIN persons p ON p.id = pr.person_id 
  JOIN roles r ON r.id = pr.role_id
  JOIN (
    SELECT p.id, MIN(CASE WHEN r.ROLE = 'Auth' THEN 0 WHEN r.ROLE in ('AAA','BBB') THEN 1 ELSE 2 END) as role_type
    FROM person_roles pr 
    JOIN persons p ON p.id = pr.person_id 
    JOIN roles r ON r.id = pr.role_id
    GROUP BY p.id
    ) rl ON rl.id = pr.person_id
ORDER BY p.person

【讨论】:

  • Koen Lostrie 你的理解是正确的。感谢您的询问。我可以知道是否可以通过仅使用一个选择语句而不是“WITH”子句来生成相同的输出?我问是因为我的实时查询中还有其他列要显示。
  • 更新了我的答案
  • 谢谢 Koen Lostrie。我接受了你的回答并投了赞成票。感谢您的宝贵时间和帮助
  • 乐于助人。 --科恩
  • 如何在另一列上做listagg?我试图在这里添加表格结构,但 stackoverflow 只有文本区域。所以在这里创建了另一个帖子stackoverflow.com/questions/67536862/…
猜你喜欢
  • 2020-08-28
  • 2013-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-18
  • 2021-08-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多