【发布时间】:2009-03-03 05:16:14
【问题描述】:
我有一个简单的表,只有名称和电子邮件,名为 name_email。
我正在尝试从中获取数据,以便: 如果两行具有相同的名称,但其中一行的电子邮件以“@yahoo.com”结尾,而另一行的电子邮件不同,则那一行的电子邮件以“@yahoo.com”结尾应该被丢弃。
获取这些数据的最佳方法是什么?
【问题讨论】:
标签: sql
我有一个简单的表,只有名称和电子邮件,名为 name_email。
我正在尝试从中获取数据,以便: 如果两行具有相同的名称,但其中一行的电子邮件以“@yahoo.com”结尾,而另一行的电子邮件不同,则那一行的电子邮件以“@yahoo.com”结尾应该被丢弃。
获取这些数据的最佳方法是什么?
【问题讨论】:
标签: sql
好的,我不会再与那些说我不应该提倡数据库架构更改的人打架了(是的,你知道你是谁 :-),但我会这样做。
1/ 如果您绝对无法更改架构,我将使用代码解决它(数据库外部真正诚实的程序代码或作为 DBMS 允许的任何语言的存储过程)。
这将检查数据库中的非雅虎名称并返回它,如果有的话。如果不存在,它将尝试返回 yahoo 名称。如果两者都不存在,它将返回一个空数据集。
2/ 如果您可以更改架构并且希望 SQL 查询来完成这项工作,我会这样做。在您的表中创建一个名为 CLASS 的单独列,对于非雅虎地址,该列预计设置为 0,雅虎地址设置为 1。
创建插入/更新触发器以检查行的每次添加或更改,根据电子邮件地址(以什么结尾)设置 CLASS。这保证了 CLASS 将始终正确设置。
当您查询表时,按名称和类对其进行排序,并且只选择第一行。这将为您提供以下首选项的电子邮件地址:非雅虎、雅虎、空数据集。
类似:
select name, email
from tbl
where name = '[name]'
order by name, class
fetch first row only;
如果您的 DBMS 没有等效于 DB2“仅获取第一行”子句,您可能仍需要编写代码来仅处理一条记录。
如果您想处理所有名称,但只处理该名称所需的特定电子邮件,这样的程序就足够了(我对尝试以过程方式使用 SQL 等关系代数的看法非常残酷,所以我不会在这里对你造成伤害):
# Get entire table contents sorted in name/class order.
resultSet = execQuery "select name, email from tbl order by name, class"
# Ensure different on first row
lastName = resultSet.value["name"] + "X"
# Process every single row returned.
while not resultSet.endOfFile:
# Only process the first in each name group (lower classes are ignored).
if resultSet.value["name"] != lastName:
processRow resultSet.value["name"] resultSet.value["email"]
# Store the last name so we can detect next name group.
lastName = resultSet.value["name"]
【讨论】:
select ne.*
from name_email ne
where ne.email not like '%@yahoo.com' escape '\' or
not exists(
select 1 from name_email
where name = ne.name and
email not like '%@yahoo.com' escape '\'
)
【讨论】:
您可以使用以下内容来排除无效的电子邮件地址:
SELECT name, email
FROM name_email
WHERE email NOT LIKE '%@yahoo.com' // % symbol is a wildcard so joe@yahoo.com and guy@yahoo.com both match this query.
AND name = 'Joe Guy';
或者这样做只包含有效的电子邮件地址或域:
SELECT name, email
FROM name_email
WHERE email LIKE '%@gmail.com'
AND name = 'Joe Guy';
如果您提前知道要查询的具体名称以及要排除或包含的电子邮件地址或域,这将非常有效。
或者,如果您不在乎返回哪个电子邮件地址,但只想返回一个,您可以使用以下内容:
SELECT DISTINCT (name, email)
FROM name_email;
【讨论】:
你可以的
SELECT TOP 1 email
FROM name_email
WHERE name = 'Joe Guy'
ORDER BY case when email like '%yahoo.com' then 1 else 0 end
因此,最后按 *@yahoo.com 排序,然后按其他排序,然后取第一个。
编辑:抱歉,误读了这个问题——你想要一个每个名字的列表,只有一封电子邮件,以及对非雅虎电子邮件的偏好。大概可以把上面的和group by一起用,我得重新考虑一下。
【讨论】:
从数据库中获取所有行,不知道名称是什么(并且不需要真正关心),但只是希望它们显示,如果匹配,如果电子邮件包含,则跳过匹配,在这种情况下, @yahoo.com
SELECT DISTINCT name, email FROM name_email
WHERE email NOT LIKE '%@yahoo.com'
GROUP BY name;
这样做会抓取所有行,但如果名称与另一行匹配,则只会抓取一条记录。但是,如果有两行名称匹配,请在电子邮件中将带有 @yahoo.com 的那一行删除。
【讨论】:
不是很漂亮,但我相信它应该可以工作
select
ne.name
,ne.email
from
name_email ne
inner join (
select
name
,count(*) as emails_per_name
from
name_email
group by name
) nec
on ne.name = nec.name
where
nec.emails_per_name = 1
or (nec.emails_per_name > 1 and ne.email not like ('%@yahoo.com'))
这是假设重复的电子邮件将在 yahoo.com 域中 - 如您的问题中所指定,如果每个名称有多个电子邮件,则这些将被排除
【讨论】:
如果您使用的是 SQL Server 2005 或 Oracle,则可以使用排名(分析)功能轻松解决您的问题。
select a.name, a.name_email
from (select name, name_email,
row_number() over (partition by name
order by case
when name_email like '%@yahoo.com' then 1
when name_email like '%@gmail.com' then 1
when ... (other 'generic' email) then 1
else 0
end) as rn) as a
where a.rn = 1
通过为各种通用电子邮件名称分配不同的值,您甚至可以拥有“首选项”。正如这里所写,如果您同时拥有 yahoo 和 gmail 地址,则无法预测会选择哪一个。
【讨论】:
您可以为此使用 UNION。选择没有 yahoo.com 的所有内容,然后只选择有 yahoo.com 且不在第一个列表中的记录。
从表中选择 DISTINCT (name, name_email) WHERE name_email 不是 '%yahoo.com' 联盟 从表中选择 DISTINCT (name, name_email) WHERE name NOT IN (SELECT DISTINCT (name, name_email) FROM TABLE WHERE name_email 不是 '%yahoo.com')
【讨论】: