tags:

views:

144

answers:

4

If I have a table as follows:

ID | Name | Category | Parent | URL | LastModified

If two rows have the same Name and parent, then they are not unique. How do I get the unique set of rows in this case (but return more than the columns that make them unique)?

So, for more details: This is a corporate keywords table, where keywords are organized by category. Each keyword can only have one category. Each keyword can have child keywords, so if parent=0 or NULL, it is a root keyword. If keywords have the same name and parent in a category, then they are not unique (regardless of the other columns). If two keywords have the same name and category, and parent=0 or NULL then they are not unique. If there are duplicates, then I only want the 1st one. The reason is I am putting these into a system that will not allow a keyword to have two children with the same name.

I would also like to see what rows ARE duplicates to see what is causing me the trouble!

Thanks a million so far for excellent responses. I am obviously not a SQL guy... :(

+4  A: 

It depends on what you want to do with non-unique rows. If you want to not have them in the result set you could use group by and having:

select Name, Parent, Max(Category) 
from Table
group by Name, Parent
having count(*) = 1

You need the Max(Category) because you aren't grouping by that column, even though there will only be one row per Name and Parent.

If, though, you want to include non-unique rows in the result, similar to:

select distinct Name, Parent, Category from Table

except that two rows with the same Name and Parent but different Category only return a single row. In that case you need to decide what to show for Category, since more than one row will be condensed down to one. You could still use Max(Category) or Min(Category) and group by, but leave off the having.

select Name, Parent, Max(Category) 
from Table
group by Name, Parent
Adam Ruth
That's only going to return one row, though, the "max" category for a given Name/Parent.
Michael Todd
Which is what he wants, all of the unique rows (based on name and parent) and the category each of those rows belongs to.
Adam Ruth
Might there not be more than one category included for a given Name/Parent? If so, then more than one category should be returned. (Guess we would need to find that out from the OP.)
Michael Todd
The way the question is worded, it seems he wants all of the unique rows. But I could be misreading it.
Adam Ruth
I do not think you should have "having count(*) = 1" clause, which will filter out ALL rows with duplicate Name + Parent, rather than leaving one of them behind.
hongliang
My reading of the question is that's what he wants, but I could be wrong, so I clarified it.
Adam Ruth
+1 Nice answer. I think it only works if you select exactly one non-grouped column. If the OP is looking for both ID and Category, this won't guarantee both come from the same row
Andomar
True. It depends on what he wants the other columns for, values from two different rows may be okay.
Adam Ruth
Thanks for all the answers! Yes, I need unique rows, as defined by name and parent (and actually by category). Even if the other values are not unique, it does not matter; just need the first one.
Donaldinio
+3  A: 

This query finds all rows where no other row has the same name and parent. If two rows have parent set to NULL, these rows are not considered to have the same parent.

SELECT T1.*
FROM Table1 T1
LEFT JOIN Table1 T2
ON T1.ID != T2.ID AND T1.Name = T2.Name AND T1.Parent = T2.Parent
WHERE T2.ID IS NULL
Mark Byers
+1 This would work also
Andomar
I'm surprised you accept this answer as it doesn't satisfy the extra specification you added 'If the rows have the same parent and name, return either.' I think Andomar's first suggestion does though.
Mark Byers
+2  A: 

You could use the row_number function to partition by Name and Parent, like:

select *
from (
    select
        row_number() over (partition by Name, Parent 
                           order by Name, Parent) as rn
    ,   *
    from YourTable
) sub
where rn = 1 -- Only first row for a name/parent combination

If you're looking to select only rows that are unique, in the sense that no other rows with the same name and parent exist, try:

select *
from YourTable a
where (
    select count(*)
    from YourTable b
    where a.Name = b.Name
    and a.Parent = b.Parent
) = 1
Andomar
+1 for the row_number suggestion. Your `order by` is rather useless - it would make more sense to order by one of the columns not in the partition, id for example. I've asked a clarifying question about this as a comment to the question but haven't received an answer yet, so it's not clear which row he wants in this situation.
Mark Byers
+1  A: 
select x,y,z
from tablename t1
where not exists (select 1 from tablename t2 where t2.name = t1.name and t1.parent = t2.parent and t2.id <> t1.id)

may run slow depending on table size

BioBuckyBall
+1 Or try `select *` :)
Andomar