Oracle: Selecting Duplicates with where clause? - sql

I have a question: Why can't I just use the following SQL query to get a list of unique eMail addresses from the PERSON table?
SELECT NOT DISTINCT Email FROM PERSON

I think the easiest and common way to achieve this is with grouping by the Email column and then keep the records having count = 1.
SELECT Email, COUNT(Email)
FROM PERSON
GROUP BY Email
HAVING COUNT(Email) > 1;
NOT DISTINCT is not working because it is not a valid expression.
DISTINCT is used to return only different values, so NOT before it is not working as you expect.

You asked to get a list of unique eMail addresses, which is done by:
SELECT DISTINCT(Email) FROM PERSON;
Standard SQL does not have NOT DISTINCT or anything like that.

Related

Count specific column in DB2

I have a table contact with three columns e.g. name, surname and age.
I would like to count the number of entries from the specific column surname.
How looks the select statement in DB2 to achieve this?
You can change the column name using as :
select count(surname) as surname_count
from contact c;
I assume you want to perform a
select count(surname)
from contact
group by surname
but you need to put some effort into the question and prove you have already researched a bit beforehand

Using EXCEPT and DISTINCT in SQL Server

I'm trying to figure out if any users in this table have multiple email addresses.
When I run the following two queries, the non-DISTINCT query has more results than the one using DISTINCT.
SELECT UserName, EmailAddress
FROM Users;
SELECT DISTINCT UserName, EmailAddress
FROM Users;
However, this query does not return any results (presumably since the remaining rows would be identical to one which is in both tables).
SELECT UserName, Payment
FROM Users
EXCEPT
SELECT DISTINCT UserName, Payment
FROM Users
How can I get the users with multiple email addresses?
Just use aggregation. If you want the list:
SELECT UserName, EmailAddress
FROM Users
GROUP BY UserName, EmailAddress
HAVING COUNT(*) > 1;
If you just want a count of duplicates, you can count the difference. Unfortunately, SQL Server doesn't allow multiple columns for COUNT(DISTINCT) but you can concatenate them. Assuming neither value is ever NULL:
SELECT COUNT(*) - COUNT(DISTINCT UserName + ' ' + EmailAddress) as numDuplicates
FROM Users;

using count and group by correctly

I have a relation CandyC(id, email, age, name, candy_id)
I want to count the CandyC.ids associated with a CandyC.candy_id once.
Attempt:
SELECT email, age, name
FROM CandyC
GROUP BY id
HAVING COUNT(DISTINCT candy_id) = 1;
It gives me an error:
not a group by expression
The group by clause need to have all the non aggregated columns selected directly. Also, it's usually a good idea to use having after the group by as it's the standard way of writing this (even though Oracle supports it the other way too).
Does this do what you want:
select email, age, name
from candyc
group by id, email, age, name
having count(distinct candy_id) = 1
If not, you should provide sample data and expected results in your question to clarify.
I think you want something more like this:
SELECT candy_id, COUNT(*)
FROM CandyC
GROUP BY candy_id;
I don't know what the email/age/name columns have to do with the question:
I want to count the CandyC.ids associated with a CandyC.candy_id once.

Unique sorted list of column values from 2 tables

I need to get a list of unique email addresses across 2 table. For example I have the selects:
select distinct
email
from
contacts
order by
email
select distinct
email
from
customers
order by
email
If I only needed one of those, piece of cake. If I wanted them as 2 columns side by side, also piece of cake.
But how do I get them as a single column, no duplicates, sorted? This will be running on Azure Sql Database if that is useful.
What about something like:
select distinct email from contacts order by email // Your first query
union
select distinct email from customers order by email // Your second query
The union creates a single column email with data from both queries and already eliminates duplicates, unlike union all.
For the ordering, just add ORDER BY email in the end.
select distinct
email
from (
select distinct email from contacts order by email
union all
select distinct email from customers order by email
) as emails

distinct sql query

I have a simple table with just name and email called name_email.
I am trying to fetch data out of it so that:
If two rows have the same name, but one has an email which is ending with ‘#yahoo.com’ and the other has a different email, then the one with the ‘#yahoo.com’ email should be discarded.
what would be best way to get this data out?
Okay, I'm not going to get involved in yet another fight with those who say I shouldn't advocate database schema changes (yes, you know who you are :-), but here's how I'd do it.
1/ If you absolutely cannot change the schema, I would solve it with code (either real honest-to-goodness procedural code outside the database or as a stored procedure in whatever language your DBMS permits).
This would check the database for a non-yahoo name and return it, if there. If not there, it would attempt to return the yahoo name. If neither are there, it would return an empty data set.
2/ If you can change the schema and you want an SQL query to do the work, here's how I'd do it. Create a separate column in your table called CLASS which is expected to be set to 0 for non-yahoo addresses and 1 for yahoo addresses.
Create insert/update triggers to examine each addition or change of a row, setting the CLASS based on the email address (what it ends in). This guarantees that CLASS will always be set correctly.
When you query your table, order it by name and class, and only select the first row. This will give you the email address in the following preference: non-yahoo, yahoo, empty dataset.
Something like:
select name, email
from tbl
where name = '[name]'
order by name, class
fetch first row only;
If your DBMS doesn't have an equivalent to the DB2 "fetch first row only" clause, you'll probably still need to write code to only process one record.
If you want to process all names but only the specific desired email for that name, a program such as this will suffice (my views on trying to use a relational algebra such as SQL in a procedural way are pretty brutal, so I won't inflict them on you here):
# 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 '\'
)
You could use something like the following to exclude invalid email addresses:
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';
Or do it like this to include only the valid email address or domain:
SELECT name, email
FROM name_email
WHERE email LIKE '%#gmail.com'
AND name = 'Joe Guy';
This works well if you know ahead of time what specific names you are querying for and what email addresses or domains you want to exclude or include.
Or if you don't care which email address you return but only want to return one, you could use something like this:
SELECT DISTINCT (name, email)
FROM name_email;
You could do
SELECT TOP 1 email
FROM name_email
WHERE name = 'Joe Guy'
ORDER BY case when email like '%yahoo.com' then 1 else 0 end
So sort them by *#yahoo.com last and anything else first, and take the first one.
EDIT: sorry, misread the question - you want a list of each name, with only one email, and a preference for non-yahoo emails. Probably can use the above along with a group by, I'll have to rethink it.
Grabbing all the rows from the database, knowing not what the names are (and not needing to care about that really), but just want them to show, and if matching, skip a match if the email contains, in this case, #yahoo.com
SELECT DISTINCT name, email FROM name_email
WHERE email NOT LIKE '%#yahoo.com'
GROUP BY name;
Doing that will grab all the rows, but only one of a record if the names match with another row. But then, if there are two rows with matching names, junk the one with #yahoo.com in the email.
Not very pretty, but I believe it should work
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'))
That is assuming that the duplicate emails would be in yahoo.com domain - as specified in your question, and those would be excluded if there is more than one email per name
If you are working with SQL Server 2005 or Oracle, you can easily solve your problem with a ranking (analytical) function.
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
By assigning different values to the various generic email names you can even have 'preferences'. As it is written here, if you have both a yahoo and a gmail address, you can't predict which one will be picked up.
You could use a UNION for this. Select everything without the yahoo.com and then just select the records that have yahoo.com and is not in the first list.
SELECT DISTINCT (name, name_email) FROM TABLE
WHERE name_email NOT '%yahoo.com'
UNION
SELECT DISTINCT (name, name_email) FROM TABLE
WHERE name NOT IN (SELECT DISTINCT (name, name_email) FROM TABLE
WHERE name_email NOT '%yahoo.com')