• subscribe
October 25, 2007 12:00 AM

Enforce Uniqueness Without the Unique Constraint

SQL Server Pro
InstantDoc ID #96785
Downloads
96785.zip

Executive Summary:

There are many ways to concatenate row values in Microsoft SQL Server databases. With the hope of finding the most efficient row-concatenation solution, a SQL Server Magazine reader tested two common approaches: using a self-reference variable within a SELECT clause and using FOR XML. He found that although these solutions produce the same result, the time it takes to get those results varies significantly.


If you’ve worked as a DBA for a while, you might have come across the problem of hitting the 900-bytes limit for unique constraints. I’ve developed a way to work around this limitation.

To work around the 900-byte limit, you can create an additional computed column that gives the checksum value of the string column. Checksum is an int data type, which means you can create a unique index for the computed column. Listing 1 shows sample code for this solution, which works on SQL Server 2005 and SQL Server 2000. In this case, a computed checksum column is created for the msg_hdr string column.

If you run the code in Listing 2, which inserts three rows without any duplicates and a fourth row with a duplicate msg_hdr, you get the error message: Msg 2601, Level 14, State 1, Line 5. Cannot insert duplicate key row in object ‘dbo.messages’ with unique index ‘msg_hdr_uk’. The statement has been terminated. As this message shows, the unique index enforces the uniqueness and fails the offending INSERT statement.

—Aviv Zucker, Intel



ARTICLE TOOLS

Comments
  • Aviv
    5 years ago
    Dec 22, 2007

    Hi Wirama,
    Regarding the case-sensitivity - you can use BINARY_CHECKSUM not on msg_hdr but rather on UPPER(msg_hdr), getting the behavior you are looking for:

    CREATE TABLE messages (
    msg_id INT NOT NULL PRIMARY KEY,
    msg_hdr VARCHAR(1024) NOT NULL,
    msg_hdr_checksum AS BINARY_CHECKSUM(UPPER(msg_hdr)) -- Note the change here
    )
    GO

    CREATE UNIQUE INDEX msg_hdr_uk ON messages(msg_hdr_checksum)
    GO

    INSERT INTO messages(msg_id, msg_hdr) VALUES (1, 'Just test')
    (1 row(s) affected)

    INSERT INTO messages(msg_id, msg_hdr) VALUES (2, 'just test')
    Msg 2601, Level 14, State 1, Line 3
    Cannot insert duplicate key row in object 'dbo.messages' with unique index 'msg_hdr_uk'.
    The statement has been terminated.

    (1 row(s) affected)

    Aviv.

  • Diana
    5 years ago
    Nov 27, 2007

    I want to thank Kasim for writing about his experience with this script. It's great when others can benefit from your experience!
    Diana

  • Kevin
    5 years ago
    Nov 27, 2007

    Hi Aviv,

    One more thing, I am wondering whether how to ignore case sensitivity as first feedback in Reader Comments column. So, if user enter "Just test" and "just test" (different case on first letter ; one is uppercase letter , the other is lowercase letter), and I would like to consider that they are same inputs so they violate unique constraint. Do you think checksum function is appropriately applied instead of binary_checksum?

    Thank you.

    Kasim Wirama, MCDBA

  • Kevin
    5 years ago
    Nov 27, 2007

    Hi Aviv,

    Thanks for your response. I tried your workaround, and it works.

    Here is a bit awkward thing I found here. Though replicate function returns same datatype as input datatype is (varchar-type input returns varchar-type output), still it has been had to be wrapped with convert/cast function to varchar to make it work properly.

    Anyway, it is the best workaround to address my case.

    Thanks for your solution.

    Kasim Wirama, MCDBA

  • Aviv
    5 years ago
    Nov 27, 2007

    Hi Kasim,
    When using REPLICATE in your inserts, you have to convert to the right data type.
    In you example, change the INSERT statements to:

    INSERT INTO messages(msg_id, msg_hdr) VALUES (1, CONVERT(VARCHAR, REPLICATE('A', 2000)))
    INSERT INTO messages(msg_id, msg_hdr) VALUES (2, CONVERT(VARCHAR, REPLICATE('B', 2000)))

    and it will work.

    Aviv.

You must log on before posting a comment.

Are you a new visitor? Register Here