如果没有匹配的记录

时间:2015-05-04 22:35:22

标签: sql postgresql sqlite

获取以下结构的表格:

ID, Email, Name

我收到了一份电子邮件列表,需要找出:表格中有哪些电子邮件以及哪些电子邮件不存在。对于在场的人,请返回每封电子邮件的相应ID。

我希望使用尽可能少的SQL语句来实现这一目标

两个问题:

  1. 由于我需要将每个给定的电子邮件与ID(如果存在)匹配,因此返回ID的顺序必须允许我将它们分别与给定的电子邮件相匹配。如果我们做一个简单的SELECT ID FROM Table WHERE Email IN (...),结果的顺序可能与为IN子句给出的电子邮件列表的顺序不匹配(至少对于SQLite)。

  2. 正常SELECT ... WHERE ... IN ()语句也不会直接告诉您哪些电子邮件不存在。我在SQLite中尝试过以下操作,虽然它运行但它并没有给我我想要的东西:SELECT COALESCE(ID, -1) AS UID, Email FROM Table WHERE Email IN (...):它只返回匹配的记录。

  3. 一个天真的解决方案是运行与给定电子邮件数量一样多的SELECT,每个电子邮件一个,以便您知道每封电子邮件是否存在记录,如果存在,则知道ID是什么。但是,如果电子邮件数量很大,这可能会导致性能问题,尤其是考虑到SQL服务器的客户端 - 服务器通信开销时。

    我对SQLite的解决方案以及大型SQL服务器的等效解决方案很感兴趣。

2 个答案:

答案 0 :(得分:1)

我想,你必须尝试这种方式。我在MSSQL透视中给出了这个答案

Crete Temp Table,并将您的电子邮件列表存储在其中。

create table #emails(
email varchar(50)
)

insert into #emails values ('b@g.com')
insert into #emails values ('c@g.com')
insert into #emails values ('d@g.com')
insert into #emails values ('e@g.com')
insert into #emails values ('f@g.com')

假设你有这样的用户表

create table users(
  id int,
  email varchar(50)
)

insert into users values (1, 'a@g.com')
insert into users values (2, 'b@g.com')
insert into users values (3, 'c@g.com')
insert into users values (4, 'd@g.com')

然后使用join来获取记录

Select #emails.email, users.id,    
       CASE WHEN users.ID IS NULL THEN 'Not Present'
            ELSE 'Present' 
       END IsPresentInTable
From #emails left join users ON #emails.email = users.email

然后输出

email   id      IsPresentInTable
b@g.com 2         Present
c@g.com 3         Present
d@g.com 4         Present
e@g.com (null)  Not Present
f@g.com (null)  Not Present

Sql Fiddle

答案 1 :(得分:1)

要解决第二个问题,使用左连接的这个技巧将起作用:

with input_emails(email) as (
values ('email1'),('email2'), ('email3')
)

select * from input_emails left join emails on input_emails.email = emails.email;

然而,不能保证电子邮件的订单会保留(但大多数情况下,如果您在电子邮件字段中有索引并且电子邮件列表没有电子邮件表那么大)。为确保订单相同,无论如何使用row_number技巧都会有所帮助:

with input_emails(email) as (
    values ('email1'),('email2'), ('email3')
),
input_emails_with_row_numbers as (
    select email, row_number() over () from input_emails
)

select * from input_emails_with_row_numbers left join emails
on input_emails_with_row_numbers.email = emails.email order by row_number;