选择已匹配n个条件中至少n-1个的所有记录

pie*_*iak 11 sql oracle oracle12c

我正在使用Oracle 12c.我想知道是否可以选择n个标准中的n-1个匹配的所有记录?

例:

CREATE TABLE users
(id number, 
firstname varchar2(100), 
lastname varchar2(100), 
city  varchar2(100));

insert into users(id, firstname, lastname, city)
 values (1, 'John', 'Smith', 'London');
insert into users(id, firstname, lastname, city)
 values (2, 'Tom',  'Smith', 'London');
insert into users(id, firstname, lastname, city)
 values (3, 'John', 'Davis', 'London');
insert into users(id, firstname, lastname, city)
 values (4, 'John', 'Smith', 'Bristol');
insert into users(id, firstname, lastname, city)
 values (5, 'Tom',  'Davis', 'London');
insert into users(id, firstname, lastname, city)
 values (6, 'Tom',  'Davis', 'Bristol');

 select * from users 
  where firstname = 'John' 
    and lastname = 'Smith'
    and city= 'London'
Run Code Online (Sandbox Code Playgroud)

此选择将仅返回一个匹配所有三个条件(id = 1)的记录.我需要的是一个查询,它返回所有匹配三个条件中至少两个的记录(id = 1,2,3,4).

如果我们知道users表有5百万个记录,那么在Oracle中是否可能?

das*_*ght 21

一般方法是将每个条件置于CASE返回1或0,并计算1的数量:

select * from users 
where (CASE WHEN firstname = 'John' THEN 1 ELSE 0 END
    + CASE WHEN lastname = 'Smith' THEN 1 ELSE 0 END
    + CASE WHEN city= 'London' THEN 1 ELSE 0 END) >= 2
Run Code Online (Sandbox Code Playgroud)

每个匹配条件对总和贡献1,因此您可以检查已满足的条件数.


Gor*_*off 9

您可以在where子句中使用表达式:

select *
from users 
where ( (case when firstname = 'John' then 1 else 0 end) +
        (case when lastname = 'Smith' then 1 else 0 end) +
        (case when city = 'London' then 1 else 0 end)
      ) = 2;
Run Code Online (Sandbox Code Playgroud)

这很容易概括,但对于3个条件和2个匹配,很容易做到:

where (firstname = 'John' and lastname = 'Smith' and city <> 'London') or
      (firstname = 'John' and lastname <> 'Smith' and city = 'London') or
      (firstname <> 'John' and lastname = 'Smith' and city = 'London')
Run Code Online (Sandbox Code Playgroud)

但是,这并不是很好.

  • OP表示*应满足至少*两个条件; 你写的是*正好*两个.如果某些列可以为空,这也可能导致第二个解决方案出现问题:例如,John-Smith-London应该与John-Smith-NULL匹配,但是您的第二个版本将拒绝它.事实上,在这种情况下,这两种解决方案并不相同. (4认同)

mat*_*guy 5

如果您经常运行这样的查询(可能是针对不同的输入firstname,lastname并且city您需要匹配),并且您需要优先考虑这些查询的性能而不是其他查询(以及DML语句的性能),您可以创建三个复合索引:on (firstname, lastname),on (firstname, city)和on (lastname, city).

然后查询应该是UNION ALL.它将读取数据三次而不是一次传递 - 但它将从索引中读取,只要在三个条件中的每一个条件中只有一小部分行匹配,就会导致更快的性能.然后,实际上只能从磁盘中读取500万行中的一小部分.

select * from users where firstname = 'John' and lastname = 'Smith'
UNION ALL
select * from users where firstname = 'John' and city = 'London'
                          and (lastname  != 'Smith' or lastname  is null)
UNION ALL
select * from users where lastname = 'Smith' and city = 'London'
                          and (firstname != 'John'  or firstname is null)
;
Run Code Online (Sandbox Code Playgroud)

您可以更改字符串匹配绑定变量,这样'John','Smith''London'(或其他值!)在运行时,而不是硬编码到查询中提供.