views:

200

answers:

4

Good Day

All we are trying to do is inside a trigger make sure the user is not inserting two fees that have 'alone' in the name. Those fees need to be handled individually.

For some reason, it appears the top section of sql quit working two weeks ago. To get around it I recoded it the second way and get the correct results. What I am confused is why would the first portion seemed to have worked for the last several years and now it does not?

SELECT  @AloneRecordCount = count(*) 

FROM  inserted i 
      INNER JOIN deleted d on i.id = d.id 

WHERE  i.StatusID = 32 
      AND d.StatusID <> 32 
      AND i.id IN  
      (SELECT settlementid FROM vwFundingDisbursement fd
      WHERE fd.DisbTypeName LIKE  '%Alone'
      AND fd.PaymentMethodID = 0)


SELECT  @AloneRecordCount = count(i.id) 

FROM    inserted i INNER JOIN
        deleted d on i.id = d.id
        JOIN vwFundingDisbursement fd on i.id = fd.settlementid

WHERE   i.StatusID = 32 
        AND d.StatusID <> 32  
        AND fd.DisbTypeName like '%Alone'
        AND fd.PaymentMethodID = 0

this is on SQL Server 2005
there is no error, instead the top statement will only return 1 or zero
while the bottom statement will return the actual number found.

+4  A: 

Have you got a null vwFundingDisbursement.settlementid value? Has that appeared recently?

araqnid
No that was one of things I looked at. Thanks for your time.
Bob Cummings
+1 Good point, "i.id IN (...)" is never true if *any* of the rows in the subselect is NULL. If one settlementid is NULL, even termporarily during processing, this has the risk of failing.
Andomar
No, IN is consecutive ORs and handles NULL. "NOT IN" fails for NULL. See this http://stackoverflow.com/questions/129077/sql-not-in-constraint-and-null-values
gbn
Thanks, I assumed it was for both
Andomar
A: 

count(*)
count(i.id)

will return different counts based on NULL values

http://stackoverflow.com/questions/59322/in-sql-whats-the-difference-between-count-and-countx

KM
But an identity column is unlikely to be NULL
Andomar
@Andomar, yea, duh didn't notice that until you pointed it out.
KM
+1  A: 

A schema (or at least how the view is created) would help, but here's a guess...

If you are looking for multiple rows in vwFundingDisbursement with the value "Alone" in the distribution type name, then a JOIN is going to return multiple rows as your source table (INSERTED) joins to multiple rows in the view. If you use IN though, SQL doesn't care if it returns multiple matches, it's only going to give you one row.

As an example:

CREATE TABLE dbo.Test_In_vs_Join1
(
     my_id     INT     NOT NULL
)

CREATE TABLE dbo.Test_In_vs_Join2
(
     my_id     INT     NOT NULL
)

INSERT INTO dbo.Test_In_vs_Join1 (my_id) VALUES (1)
INSERT INTO dbo.Test_In_vs_Join1 (my_id) VALUES (2)
INSERT INTO dbo.Test_In_vs_Join1 (my_id) VALUES (3)
INSERT INTO dbo.Test_In_vs_Join1 (my_id) VALUES (4)
INSERT INTO dbo.Test_In_vs_Join1 (my_id) VALUES (5)

INSERT INTO dbo.Test_In_vs_Join2 (my_id) VALUES (1)
INSERT INTO dbo.Test_In_vs_Join2 (my_id) VALUES (1)
INSERT INTO dbo.Test_In_vs_Join2 (my_id) VALUES (2)
INSERT INTO dbo.Test_In_vs_Join2 (my_id) VALUES (3)
INSERT INTO dbo.Test_In_vs_Join2 (my_id) VALUES (3)

SELECT
     T1.my_id,
     COUNT(*)
FROM
     dbo.Test_In_vs_Join1 T1
INNER JOIN dbo.Test_In_vs_Join2 T2 ON
     T2.my_id = T1.my_id
GROUP BY
    T1.my_id

SELECT
     T1.my_id,
     COUNT(*)
FROM
     dbo.Test_In_vs_Join1 T1
WHERE
    T1.my_id IN (SELECT T2.my_id FROM dbo.Test_In_vs_Join2 T2)
GROUP BY
    T1.my_id

On a side note, burying a column inside of another column like this is a violation of the normalized form and just asking for problems. Performing this kind of business logic in a trigger is also a dangerous path to go down as you're finding out.

Tom H.
I am not sure what you mean by burying a column, but I certainly agree and understand the idea of not putting business logic in a database. And I do not like triggers at all, but it was not my decision.
Bob Cummings
This might be the right solution; the IN clause will not find multiple rows in vwFundingDisbursement, but a JOIN will. The buried column is probably the Alone at the end of DisbTypeName. It should be its own column.
Andomar
What I mean by burying a column is that you seem to have a character column, DisbTypeName and within that column you are storing a flag of whether or not the fee can be inserted with other fees of the same type.
Tom H.
Actually this is exactly the kind of logic that belongs in the database if you don't want data integrity issues. Doing this is the App would be a poor choice as it would not cover any updates made outside the app. However even in the database, this trigger is not how I would handle because LIKE '%Alone' can't use an index. I'd put the alone status in it's own column and then put a unique index that covers that column and the id field.
HLGEM
This kind of validation belongs in both the database (keep the data clean) and the application (provide a good validation UI), redundant validation is not necessarily a bad thing. Although in some cases you can argue for multiple levels of strictness, with the DB enforcing just a lowest common level.
araqnid
Just for the record, I'm actually FOR putting business logic in the database when it deals with the state of the data (as opposed to business processes). I just don't think that triggers are a good place for it. Declarative is best, with logic in stored procedures and locked down tables being the next level.
Tom H.
A: 

I assume this MSSQL(?) and it is handling an UPDATE statement since you're joining both the inserted/deleted tables?

Which table is this trigger on?

micahtan