Finding duplicated data in a case insensitive column

The other day, I had a problem with some data that I never dreamed I would ever see. In a case insensitive database, in a table’s column that was case insensitive, the customer was using the data as case sensitive. Firstly, let’s just go ahead and say it. “This was a sucky implementation.” But as is common, in my typical role as a data architect in the data warehousing team, I get to learn all sorts of interesting techniques for finding and dealing with “data” that has been used in “interesting” ways.

What is kind of interesting is actually figuring out what that duplicated data was. The case that I was dealing with wasn’t a kind of useful packed surrogate value, where you may use a base 62 number, with a-z, A-Z and 0-9 as characters. So 1, 2, … , 9, 0, a, b, c, … x, y, z, A, B.. etc. 1A1 is a different value in that sequence than 1a1, and is greater . Neat technique, and one that I have been threatening to develop using a SEQUENCE object, where you can pack in a lot of sequential data in a small number of bytes. No, this wasn’t a useful case such as this, in this case, one value was lower case, another had leading capitals. So perhaps “active customer” and “Active Customer”. Yeah, seriously, they meant different things.

Note: The query I will use will help to find the permutations of values that you have in your data (Like “United States”, ‘UNITED STATES” for example.) Hence this is not a completely esoteric exercise.

To find the data, I figured I would use a case sensitive collation, but it isn’t as straightforward as grouping on the data using a collation. For a sample set of data, I will use the following:

Now, because it is case insensitive, to the normal user, you can’t really tell the difference, but in this contrived set, we will have > 1 row with the same color value (in my real example, there were thousands).

This returns:

ColorName
-------------------- -----------
Azure                2
beige                2
Black                2

Now, whether or not “beige” and “Beige” are bizarrely enough different things, or just misformatted data, this is not what we want back. We want to treat them as different, so using a case sensitive collation (you need the COLLATE argument in the SELECT clause if you have it in the GROUP BY clause:

This doesn’t return anything. Hmm. The trick is in the HAVING clause. Use COUNT DISTINCT on the column as case sensitive instead of the GROUP BY and we get the items we want, but still just one per:

The output is the same as we had previously, with 2 rows per color (though this time they are all the lowercase items). However, now the ColorName column returned by the SELECT clause is case insensitive, so we can use it to get the case insensitive duplicates

Which returns the 6 rows with the duplicates we are hunting for:

ColorID     ColorName
----------- --------------------
1           Azure
2           Beige
3           Black
37          azure
38          beige
39          black

With this set, you could then group on the values using the case sensitive collation to see how many duplicates you have, such as:

Easy enough, but hopefully if you get stuck doing this some day, this will help you get going.