views:

411

answers:

5

I have two tables. indRailType contains a list of the names paired with an ID value that I use in other tables to indicate the rail type. WO_BreakerRail contains a date column and a rail code colume that corresponds to the same code in indRailType and some other data. There's a row in WO_BreakerRail for any activity on each rail type, for every date. So I could have 3 rows dated for 3/19/2010, each row indicates a different rail code, and what happened.

When I use the following LEFT OUTER JOIN, I get a table with all the types of rail, with nulls in the rows where nothing happened on the 19th. Now, this is only working because I only have one date represented in my WO_BreakerRail table right now, the 19th. When I add more rows with different dates, things will go haywire.

This is my SQL statement, which right now gives me exactly the results I want:

SELECT        WO_BreakerRail.ID, indRailType.RailType, WO_BreakerRail.CreatedPieces, WO_BreakerRail.OutsideSource, WO_BreakerRail.Charged, WO_BreakerRail.Rejected, WO_BreakerRail.RejectedToCrop
FROM            indRailType LEFT OUTER JOIN
                         WO_BreakerRail ON indRailType.RailCode = WO_BreakerRail.RailCode

Now, when I add in a WHERE WO_BreakerRail.Date = @Date clause I lose all the rows in the JOIN which nothing happened. I don't want that. From reading up, it sounds like a FULL OUTER JOIN is what I want, but SQL Server Compact Edition doesn't support FULL OUTER JOINs. Is there a way around this, or am I looking for something else entirely?

+1  A: 

add the WO_BreakerRail.Date = @Date on the LEFT OUTER JOIN statement and not the WHERE:

SELECT
    WO_BreakerRail.ID, indRailType.RailType, WO_BreakerRail.CreatedPieces, WO_BreakerRail.OutsideSource, WO_BreakerRail.Charged, WO_BreakerRail.Rejected, WO_BreakerRail.RejectedToCrop
    FROM indRailType 
        LEFT OUTER JOIN WO_BreakerRail ON indRailType.RailCode = WO_BreakerRail.RailCode AND WO_BreakerRail.Date = @Date

or you can add it to the WHERE:

SELECT
    WO_BreakerRail.ID, indRailType.RailType, WO_BreakerRail.CreatedPieces, WO_BreakerRail.OutsideSource, WO_BreakerRail.Charged, WO_BreakerRail.Rejected, WO_BreakerRail.RejectedToCrop
    FROM indRailType 
        LEFT OUTER JOIN WO_BreakerRail ON indRailType.RailCode = WO_BreakerRail.RailCode
    WHERE (WO_BreakerRail.Date = @Date OR WO_BreakerRail.Date IS NULL)
KM
+2  A: 

You could use this where clause:

WHERE WO_BreakerRail.Date = @Date OR WO_BreakerRail.Date IS NULL
Andrew Bezzub
When working with outer joins, it's best to factor any "isnull work" in the ON clause, and not the where clause
Philip Kelley
+4  A: 

Try:

SELECT        WO_BreakerRail.ID, indRailType.RailType, WO_BreakerRail.CreatedPieces, WO_BreakerRail.OutsideSource, WO_BreakerRail.Charged, WO_BreakerRail.Rejected, WO_BreakerRail.RejectedToCrop
FROM            indRailType LEFT OUTER JOIN
                         WO_BreakerRail ON indRailType.RailCode = WO_BreakerRail.RailCode
                AND WO_BreakerRail.Date = @Date

Thus adding AND WO_BreakerRail.Date = @Date onto the join

Philip Fourie
Don't forget to factor in the "time" portion of @Date. If values can vary between midnight and midnight, you'll have to use some form of between clause.
Philip Kelley
This answer worked perfectly, thank you.Philip: Thanks for that pointer, although I ran into that problem earlier in this project which stumped me for awhile, but I ended up fixing it in my C# code, removing all time portions, defaulting every date to midnight.
Wesley
@Wesley, it is best to "floor" datetime data types to remove time, so the time is 00:00:00.000 and not midnight.
KM
Yep--I recommend doing this in the database, in case some other application is passing in "unclean" data.
Philip Kelley
+1  A: 

The predicate condition needs to be in the On clause for the join, not in the where clause. The way Outer joins work, is after the join conditions are analyzed, all the rows from the "outer side" that do not match the inner side are added back in.... But this all happens before the where clause is processed. So if the where clause predicate filters on an attribute from the outer side of an outer join, all those rows will be removed again... (They are all null). Put the predicate in the join condition instead, then it will get evaluated before the missing rows are added back in...

SELECT b.ID, t.RailType, b.CreatedPieces, 
       b.OutsideSource, b.Charged, b.Rejected, 
       b.RejectedToCrop 
FROM indRailType t
   LEFT JOIN WO_BreakerRail b
        ON t.RailCode = b.RailCode 
            And b.Date = @Date
Charles Bretana
A: 

What you want is to filter your WO_BreakerRail to the desired date before LEFT JOINing it to indRailType. If you just add the WHERE statement, it is done the other way around, which results in the problem you described.

The solutions provided so far should work (move the condition into the LEFT JOIN), but I'd like to suggest an alternative: You can use a subquery to specify the order in which things should be done:

SELECT R.ID, indRailType.RailType, R.CreatedPieces, R.OutsideSource, R.Charged, R.Rejected, R.RejectedToCrop
  FROM indRailType LEFT OUTER JOIN
       (SELECT * FROM WO_BreakerRail WHERE Date = @Date) R
       ON indRailType.RailCode = R.RailCode

(Untested, but as far as I know, SQL Server CE supports subqueries.)

Heinzi