【问题标题】:Select default value if no matched record如果没有匹配的记录,则选择默认值
【发布时间】:2015-05-04 22:35:21
【问题描述】:

取一个如下结构的表:

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 (...):它只返回匹配的记录。

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

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

【问题讨论】:

  • 在表中插入值列表,然后使用左连接

标签: sql postgresql sqlite


【解决方案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;

【讨论】:

  • 感谢您的回答。这是哪个 SQL 引擎?第一个with 语句是否可用于所有主要的 SQL 实现?
  • 它是 postgresql。但是with 表达式(称为公用表表达式)和窗口函数(row_number)应该在主要的 RDBMS(oracle、mssql)中可用,但语法可能不同。不确定values 表达式的可用性。
【解决方案2】:

我认为,你必须尝试这种方式。我从 MSSQL 的角度给出了这个答案

克里特临时表,并将您的电子邮件列表存储在其中。

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

【讨论】:

  • 欣赏答案,但对我来说似乎有点适得其反,我们需要进行大量插入,然后在两个表上进行连接选择。我怀疑这些插入的成本可能不会比那些个人选择的低多少?
  • 我的问题是您需要与表格匹配的电子邮件列表在哪里?在 SQL 中或用户输入该列表意味着您在应用程序级别?
  • 好问题,电子邮件列表来自另一个内部服务,每个请求的电子邮件查找范围可能从几百到几千不等。所以你可以说它们来自应用程序级别,但仍然存在性能问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多