From: Steve Kass on
Eric,

You can avoid the assumption on VendorToAddressID in one of the
following ways, each of which allows VendorUseForMailing_UNIQUE
to equal VendorToAddressID for non-mailing addresses by using a
value not in the key domain (INT NOT NULL) in the mailing address
case.

One possibility is to take advantage of the fact that SQL Server
violates the SQL standard when it comes to UNIQUE constraints
and NULLs. By design, SQL Server allows only one NULL value
in a column with a UNIQUE constraint, so you could use NULL
as the extra value, since it can't be a key value:

VendorUseForMailing_UNIQUE AS (CASE UseForMailing
WHEN 1 THEN NULL
ELSE VendorToAddressID
END)

Alternatively (to preserve forward compatibility should SQL Server
ever implement ANSI UNIQUE constraints), use a domain for
VendorUseForMailing_UNIQUE that contains all INT NOT NULL
values and more:

VendorUseForMailing_UNIQUE AS (CASE UseForMailing
WHEN 1 THEN CAST(10000000000 AS BIGINT)
ELSE VendorToAddressID
END)

Steve Kass
Drew University
http://www.stevekass.com

Eric Isaacs wrote:

>Erland,
>
>I think my suggestion would work, assuming that the VendorToAddressID
>was a unique positive number (INT IDENTITY, or just a unique INT
>that's greater than zero.)
>
>Here's a working example with comments. The second insert should
>fail, but all the others should succeed.
>
>CREATE TABLE VendorToAddress
>(
> VendorToAddressID INT IDENTITY NOT NULL,
> VendorID INT NOT NULL,
> AddressID INT NOT NULL,
> UseForMailing BIT NOT NULL,
> UseForPurchaseOrder BIT NOT NULL,
> UseForChecks BIT NOT NULL,
> VendorUseForMailing_UNIQUE AS (CASE UseForMailing
> WHEN 1 THEN 1
> ELSE -VendorToAddressID
> END)
> CONSTRAINT pk_vendaddr PRIMARY KEY (VendorToAddressID),
> CONSTRAINT u_vendoraddress UNIQUE (VendorID, AddressID),
> CONSTRAINT u_VendorMailingAddress UNIQUE (VendorID,
>VendorUseForMailing_UNIQUE)
>)
>
>GO
> SET NOCOUNT OFF
>
> PRINT 'This next insert should succeed.'
>
> INSERT
> VendorToAddress
> (
> VendorID,
> AddressID,
> UseForMailing,
> UseForPurchaseOrder,
> UseForChecks
> )
> VALUES
> (1, 1, 1, 1, 1)
>
>
>GO
> PRINT 'This next insert should fail because a vendor can have only
>one address used for mailing.'
>
> INSERT
> VendorToAddress
> (
> vendorid,
> addressid,
> useformailing,
> UseForPurchaseOrder,
> useforchecks
> )
> VALUES
> (1, 2, 1, 1, 1)
>
>GO
> PRINT 'This next two inserts should succeed because a vendor can
>have multiple addresses, as long as they are different and only one is
>used for mailing.'
>
> INSERT
> VendorToAddress
> (
> vendorid,
> addressid,
> useformailing,
> UseForPurchaseOrder,
> useforchecks
> )
> VALUES
> (1, 2, 0, 1, 1)
>
>
>GO
> INSERT
> VendorToAddress
> (
> vendorid,
> addressid,
> useformailing,
> UseForPurchaseOrder,
> useforchecks
> )
> VALUES
> (1, 4, 0, 1, 1)
>
>GO
> PRINT 'This next three inserts should succeed because another
>vendor can have one mailing address and multiple non-mailing
>addresses.'
>
> INSERT
> VendorToAddress
> (
> vendorid,
> addressid,
> useformailing,
> UseForPurchaseOrder,
> useforchecks
> )
> VALUES
> (2, 5, 1, 1, 1)
>
>GO
> INSERT
> VendorToAddress
> (
> vendorid,
> addressid,
> useformailing,
> UseForPurchaseOrder,
> useforchecks
> )
> VALUES
> (2, 6, 0, 1, 1)
>GO
> INSERT
> VendorToAddress
> (
> vendorid,
> addressid,
> useformailing,
> UseForPurchaseOrder,
> useforchecks
> )
> VALUES
> (2, 7, 0, 0, 0)
>
>GO
> SET NOCOUNT ON
> SELECT * FROM VendorToAddress
>
>GO
> DROP TABLE VendorToAddress
>
>
From: Steve Kass on
Thanks, Alex. While I suggested the name "nullbuster" for the computed
column, Ivan Arjentinski posted the idea a few months before I did.

SK

Alex Kuznetsov wrote:

>
> it certainly does work, and it is well known. Google up the
> following : nullbuster steve kass
>